From 07b5a5d86cd3266f3099e9ddd9f0b64da1a27b9e Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 27 Jul 2026 18:08:26 +0200 Subject: [PATCH 01/17] feat(albwaf): Onboard custom rule groups relates to STACKITTPR-748 --- .../services/albwaf/albwaf_acc_test.go | 351 ++++++++ .../albwaf/custom_rule_group/datasource.go | 236 +++++ .../albwaf/custom_rule_group/resource.go | 822 ++++++++++++++++++ .../albwaf/custom_rule_group/resource_test.go | 344 ++++++++ .../albwaf/managed_rule_set/resource.go | 4 +- .../albwaf/testdata/custom-rule-group-max.tf | 42 + .../albwaf/testdata/custom-rule-group-min.tf | 29 + stackit/provider.go | 3 + 8 files changed, 1829 insertions(+), 2 deletions(-) create mode 100644 stackit/internal/services/albwaf/custom_rule_group/datasource.go create mode 100644 stackit/internal/services/albwaf/custom_rule_group/resource.go create mode 100644 stackit/internal/services/albwaf/custom_rule_group/resource_test.go create mode 100644 stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf create mode 100644 stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index b8e539850..3de990be9 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -22,10 +22,55 @@ import ( ) var ( + //go:embed testdata/custom-rule-group-min.tf + customRuleGroupMinConfig string + + //go:embed testdata/custom-rule-group-max.tf + customRuleGroupMaxConfig string + //go:embed testdata/managed-rule-set.tf managedRuleSetConfig string ) +var testCustomRuleGroupMin = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "action": config.StringVariable("ACTION_DENY"), + "operator_type": config.StringVariable("OPERATOR_VALIDATE_UTF8_ENCODING"), + "operator_value": config.StringVariable("foo"), + "transformation": config.StringVariable("TRANSFORMATION_LOWERCASE"), + "variable_type": config.StringVariable("VARIABLE_RESPONSE_STATUS"), +} + +var testCustomRuleGroupMinUpdated = func() config.Variables { + updatedConfig := config.Variables{} + maps.Copy(updatedConfig, testCustomRuleGroupMin) + updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"]))) + return updatedConfig +} + +var testCustomRuleGroupMax = config.Variables{ + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "description": config.StringVariable("foo bar"), + "action": config.StringVariable("ACTION_DENY"), + "log": config.BoolVariable(true), + "log_msg": config.StringVariable("foo-bar"), + "operator_type": config.StringVariable("OPERATOR_CONTAINS"), + "operator_value": config.StringVariable("foo"), + "transformation": config.StringVariable("TRANSFORMATION_LOWERCASE"), + "variable_type": config.StringVariable("VARIABLE_REQUEST_HEADERS"), + "variable_value": config.StringVariable("bar"), +} + +var testCustomRuleGroupMaxUpdated = func() config.Variables { + updatedConfig := config.Variables{} + maps.Copy(updatedConfig, testCustomRuleGroupMax) + updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"]))) + // updatedConfig["log"] = config.BoolVariable(false) + return updatedConfig +} + var testManagedRuleSet = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), "name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), @@ -39,6 +84,275 @@ var testManagedRuleSetUpdated = func() config.Variables { return updatedConfig } +func TestAccCustomRuleGroupMin(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckDestroy, + Steps: []resource.TestStep{ + // Creation + { + ConfigVariables: testCustomRuleGroupMin, + Config: fmt.Sprintf("%s\n%s", testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMinConfig), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "id"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMin["name"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["variable_type"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Data source + { + ConfigVariables: testCustomRuleGroupMin, + Config: fmt.Sprintf(` + %s + %s + + data "stackit_alb_waf_custom_rule_group" "custom_rule_group" { + project_id = stackit_alb_waf_custom_rule_group.custom_rule_group.project_id + name = stackit_alb_waf_custom_rule_group.custom_rule_group.name + } + `, + testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMinConfig, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "id", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "id", + ), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMin["name"])), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", + ), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), + // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + ), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["variable_type"])), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Import + { + ConfigVariables: testCustomRuleGroupMin, + ResourceName: "stackit_alb_waf_custom_rule_group.custom_rule_group", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_alb_waf_custom_rule_group.custom_rule_group"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_alb_waf_custom_rule_group.custom_rule_group") + } + policyId, ok := r.Primary.Attributes["name"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, policyId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, + // Update + { + ConfigVariables: testCustomRuleGroupMinUpdated(), + Config: fmt.Sprintf("%s\n%s", testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMinConfig), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_alb_waf_custom_rule_group.custom_rule_group", plancheck.ResourceActionReplace), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "id"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["name"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["action"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["operator_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["variable_type"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Deletion is done by the framework implicitly + }, + }) +} + +func TestAccCustomRuleGroupMax(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + CheckDestroy: testAccCheckDestroy, + Steps: []resource.TestStep{ + // Creation + { + ConfigVariables: testCustomRuleGroupMax, + Config: fmt.Sprintf("%s\n%s", testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMaxConfig), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "id"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMax["name"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMax["description"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_value"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMax["transformation"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_value"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Data source + { + ConfigVariables: testCustomRuleGroupMax, + Config: fmt.Sprintf(` + %s + %s + + data "stackit_alb_waf_custom_rule_group" "custom_rule_group" { + project_id = stackit_alb_waf_custom_rule_group.custom_rule_group.project_id + name = stackit_alb_waf_custom_rule_group.custom_rule_group.name + } + `, + testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMaxConfig, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "id", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "id", + ), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMax["name"])), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMax["description"])), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", + ), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), + // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + resource.TestCheckResourceAttrPair( + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + ), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_type"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_value"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "1"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMax["transformation"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_type"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_value"])), + + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Import + { + ConfigVariables: testCustomRuleGroupMax, + ResourceName: "stackit_alb_waf_custom_rule_group.custom_rule_group", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_alb_waf_custom_rule_group.custom_rule_group"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_alb_waf_custom_rule_group.custom_rule_group") + } + policyId, ok := r.Primary.Attributes["name"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, policyId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, + // Update + { + ConfigVariables: testCustomRuleGroupMaxUpdated(), + Config: fmt.Sprintf("%s\n%s", testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMaxConfig), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_alb_waf_custom_rule_group.custom_rule_group", plancheck.ResourceActionReplace), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "project_id", testutil.ProjectId), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "region", testutil.Region), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "id"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["name"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["description"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["action"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log_msg"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["operator_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.value", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["operator_value"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "1"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["transformation"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["variable_type"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["variable_value"])), + + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), + ), + }, + // Deletion is done by the framework implicitly + }, + }) +} + func TestAccManagedRuleSet(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, @@ -53,6 +367,7 @@ func TestAccManagedRuleSet(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "region", testutil.Region), resource.TestCheckResourceAttrSet("stackit_alb_waf_managed_rule_set.managed_rule_set", "id"), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSet["name"])), + resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSet["type"])), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), @@ -79,6 +394,7 @@ func TestAccManagedRuleSet(t *testing.T) { "stackit_alb_waf_managed_rule_set.managed_rule_set", "id", ), resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSet["name"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSet["type"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), @@ -115,6 +431,7 @@ func TestAccManagedRuleSet(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "region", testutil.Region), resource.TestCheckResourceAttrSet("stackit_alb_waf_managed_rule_set.managed_rule_set", "id"), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSetUpdated()["name"])), + resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSetUpdated()["type"])), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), @@ -135,6 +452,7 @@ func createClient() (*albwaf.APIClient, error) { func testAccCheckDestroy(s *terraform.State) error { checkFunctions := []func(s *terraform.State) error{ + testAlbWafCustomRuleGroupDestroy, testAlbWafManagedRuleSetDestroy, } var errs []error @@ -150,6 +468,39 @@ func testAccCheckDestroy(s *terraform.State) error { return errors.Join(errs...) } +func testAlbWafCustomRuleGroupDestroy(s *terraform.State) error { + ctx := context.Background() + client, err := createClient() + if err != nil { + return err + } + + customRuleGroupsToDestroy := []string{} + for _, rs := range s.RootModule().Resources { + if rs.Type != "stackit_alb_waf_custom_rule_group" { + continue + } + // custom rule group transform id: "[projectId],[region],[name]" + name := strings.Split(rs.Primary.ID, core.Separator)[2] + customRuleGroupsToDestroy = append(customRuleGroupsToDestroy, name) + } + + resp, err := client.DefaultAPI.ListCustomRuleGroup(ctx, testutil.ProjectId, testutil.Region).Execute() + if err != nil { + return fmt.Errorf("getting resp: %w", err) + } + + for _, item := range resp.Items { + if utils.Contains(customRuleGroupsToDestroy, item.GetName()) { + _, err := client.DefaultAPI.DeleteCustomRuleGroup(ctx, testutil.ProjectId, testutil.Region, item.GetName()).Execute() + if err != nil { + return fmt.Errorf("deleting policy %s during CheckDestroy: %w", item.GetName(), err) + } + } + } + return nil +} + func testAlbWafManagedRuleSetDestroy(s *terraform.State) error { ctx := context.Background() client, err := createClient() diff --git a/stackit/internal/services/albwaf/custom_rule_group/datasource.go b/stackit/internal/services/albwaf/custom_rule_group/datasource.go new file mode 100644 index 000000000..b10c9ec94 --- /dev/null +++ b/stackit/internal/services/albwaf/custom_rule_group/datasource.go @@ -0,0 +1,236 @@ +package custom_rule_group + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/albwaf/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +var ( + _ datasource.DataSource = &customRuleGroupDataSource{} + _ datasource.DataSourceWithConfigure = &customRuleGroupDataSource{} +) + +type customRuleGroupDataSource struct { + client *albWaf.APIClient + providerData core.ProviderData +} + +func NewCustomRuleGroupDataSource() datasource.DataSource { + return &customRuleGroupDataSource{} +} + +func (r *customRuleGroupDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + var ok bool + r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &r.providerData, &resp.Diagnostics, "stackit_alb_waf_custom_rule_group", core.Resource) + if resp.Diagnostics.HasError() { + return + } + + apiClient := utils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "ALB WAF client configured") +} + +func (r *customRuleGroupDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_alb_waf_custom_rule_group" +} + +func (r *customRuleGroupDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: features.AddBetaDescription(fmt.Sprintf("ALB WAF Custom Rule Group resource schema. %s", core.ResourceRegionFallbackDocstring), core.Resource), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "region": schema.StringAttribute{ + Description: descriptions["region"], + Optional: true, + Computed: true, + }, + "name": schema.StringAttribute{ + Description: descriptions["name"], + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^[0-9a-z](?:(?:[0-9a-z]|-){0,61}[0-9a-z])?$`), + "must start and end with an alphanumeric character, may contain hyphens, and be 1-63 characters long", + ), + }, + }, + "rules": schema.ListNestedAttribute{ + Description: descriptions["rules"], + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "behaviour": schema.SingleNestedAttribute{ + Description: descriptions["behaviour"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "action": schema.StringAttribute{ + Description: descriptions["behaviour_action"], + Computed: true, + }, + "log": schema.BoolAttribute{ + Description: descriptions["behaviour_log"], + Computed: true, + }, + "log_msg": schema.StringAttribute{ + Description: descriptions["behaviour_log_msg"], + Computed: true, + }, + "severity": schema.StringAttribute{ + Description: descriptions["behaviour_severity"], + Computed: true, + }, + }, + }, + "conditions": schema.ListNestedAttribute{ + Description: descriptions["rule_conditions"], + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "operator": schema.SingleNestedAttribute{ + Description: descriptions["operator"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Description: descriptions["operator_type"], + Computed: true, + }, + "value": schema.StringAttribute{ + Description: descriptions["operator_value"], + Computed: true, + }, + }, + }, + "transformations": schema.ListAttribute{ + Description: descriptions["transformations"], + Computed: true, + ElementType: types.StringType, + }, + "variable": schema.SingleNestedAttribute{ + Description: descriptions["variable"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Description: descriptions["variable_type"], + Computed: true, + }, + "value": schema.StringAttribute{ + Description: descriptions["variable_value"], + Computed: true, + }, + }, + }, + }, + }, + }, + "description": schema.StringAttribute{ + Description: descriptions["rule_description"], + Computed: true, + }, + "id": schema.Int32Attribute{ + Description: descriptions["rule_id"], + Computed: true, + }, + }, + }, + }, + "usage": schema.SingleNestedAttribute{ + Description: descriptions["usage"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "count": schema.Int32Attribute{ + Description: descriptions["usage_count"], + Computed: true, + }, + "items": schema.ListAttribute{ + Description: descriptions["usage_items"], + Computed: true, + ElementType: types.StringType, + }, + }, + }, + }, + } +} + +func (r *customRuleGroupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.Config.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + name := model.Name.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "name", name) + + customRuleGroupResp, err := r.client.DefaultAPI.GetCustomRuleGroup(ctx, projectId, region, name).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + core.LogAndAddError(ctx, &resp.Diagnostics, fmt.Sprintf("ALB WAF Custom Rule Group with name %q not found in project %q and region %q", name, projectId, region), err.Error()) + resp.State.RemoveResource(ctx) + return + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading ALB WAF Custom Rule Group", err.Error()) + return + } + + ctx = core.LogResponse(ctx) + + err = mapFields(ctx, customRuleGroupResp, &model, region) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading ALB WAF Custom Rule Group", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "ALB WAF Custom Rule Group read") +} diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go new file mode 100644 index 000000000..7479b63bd --- /dev/null +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -0,0 +1,822 @@ +package custom_rule_group + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + + sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" + + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/albwaf/utils" + tfutils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" +) + +var ( + _ resource.Resource = &customRuleGroupResource{} + _ resource.ResourceWithConfigure = &customRuleGroupResource{} + _ resource.ResourceWithImportState = &customRuleGroupResource{} + _ resource.ResourceWithModifyPlan = &customRuleGroupResource{} + + variableTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionVariableTypeEnumValues) + transformationOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionTransformationsInnerEnumValues) + operatorTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionOperatorTypeEnumValues) + actionOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedBehaviourActionEnumValues) +) + +type Model struct { + Id types.String `tfsdk:"id"` // needed by TF + ProjectId types.String `tfsdk:"project_id"` + Region types.String `tfsdk:"region"` + Name types.String `tfsdk:"name"` + Rules types.List `tfsdk:"rules"` + Usage types.Object `tfsdk:"usage"` +} + +type RuleModel struct { + Behaviour types.Object `tfsdk:"behaviour"` + Conditions types.List `tfsdk:"conditions"` + Description types.String `tfsdk:"description"` + Id types.Int32 `tfsdk:"id"` +} + +var ruleType = map[string]attr.Type{ + "behaviour": types.ObjectType{AttrTypes: behaviourType}, + "conditions": types.ListType{ + ElemType: types.ObjectType{AttrTypes: conditionType}, + }, + "description": types.StringType, + "id": types.Int32Type, +} + +type BehaviourModel struct { + Action types.String `tfsdk:"action"` + Log types.Bool `tfsdk:"log"` + LogMsg types.String `tfsdk:"log_msg"` + Severity types.String `tfsdk:"severity"` +} + +var behaviourType = map[string]attr.Type{ + "action": types.StringType, + "log": types.BoolType, + "log_msg": types.StringType, + "severity": types.StringType, +} + +type ConditionModel struct { + Operator types.Object `tfsdk:"operator"` + Transformations types.List `tfsdk:"transformations"` + Variable types.Object `tfsdk:"variable"` +} + +var conditionType = map[string]attr.Type{ + "operator": types.ObjectType{AttrTypes: operatorType}, + "transformations": types.ListType{ElemType: types.StringType}, + "variable": types.ObjectType{AttrTypes: variableType}, +} + +type OperatorModel struct { + Type types.String `tfsdk:"type"` + Value types.String `tfsdk:"value"` +} + +var operatorType = map[string]attr.Type{ + "type": types.StringType, + "value": types.StringType, +} + +type VariableModel struct { + Type types.String `tfsdk:"type"` + Value types.String `tfsdk:"value"` +} + +var variableType = map[string]attr.Type{ + "type": types.StringType, + "value": types.StringType, +} + +type UsageModel struct { + Count types.Int32 `tfsdk:"count"` + Items types.List `tfsdk:"items"` +} + +var usageType = map[string]attr.Type{ + "count": types.Int32Type, + "items": types.ListType{ElemType: types.StringType}, +} + +type customRuleGroupResource struct { + client *albWaf.APIClient + providerData core.ProviderData +} + +func NewCustomRuleGroupResource() resource.Resource { + return &customRuleGroupResource{} +} + +func (r *customRuleGroupResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + var ok bool + r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) + if !ok { + return + } + + features.CheckBetaResourcesEnabled(ctx, &r.providerData, &resp.Diagnostics, "stackit_alb_waf_custom_rule_group", core.Resource) + if resp.Diagnostics.HasError() { + return + } + + apiClient := utils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + r.client = apiClient + tflog.Info(ctx, "ALB WAF client configured") +} + +func (r *customRuleGroupResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_alb_waf_custom_rule_group" +} + +// descriptions for the attributes in the Schema. +var descriptions = map[string]string{ + "id": "Terraform's internal resource identifier. Structured as \"`project_id`,`region`,`name`\".", + "project_id": "STACKIT project ID associated with the ALB WAF Custom Rule Group.", + "region": "STACKIT region name the resource is located in. If not defined, the provider region is used.", + "name": "Custom rule group configuration name.", + "rules": "Enriched rules containing auto-generated IDs and computed severity values.", + "rule_behaviour": "Behaviour of the rule.", + "rule_condition": "Conditions for this rule (order matters, first condition match triggers execution).", + "rule_description": "A clear description explaining the threat vector or criteria addressed by this rule.", + "rule_id": "Backend auto-allocated unique rule ID within the valid 1-99999 threshold.", + "behaviour_action": "The protective stance action. ACTION_DENY forces a 403 status response code.", + "behaviour_log": "Determines whether an entry should be generated in the security ledger upon a rule hit.", + "behaviour_log_msg": "Custom notification message string mapped to underlying logdata contexts. Required if log is true.", + "behaviour_severity": "Severity classification metric used by internal analytics graphs.", + "operator": "The comparison logic executed against the transformed variable.", + "operator_type": "The operational evaluation type definition macro.", + "operator_value": "The text or rule regex pattern arguments applied inside the operator execution loop.", + "transformations": "Ordered normalization steps applied before the operator runs.", + "variable": "The part of the HTTP transaction to inspect.", + "variable_type": "The targeted validation engine variable macro.", + "variable_value": "Optional key element context for map variables (e.g., matching a 'Host' header key).", + "usage": "Tracking metrics for CRG resource utilization.", + "usage_count": "Number of WAF configurations actively using this rule group.", + "usage_items": "List of individual WAF configuration names that bind this rule group.", +} + +func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: features.AddBetaDescription(fmt.Sprintf("ALB WAF Custom Rule Group resource schema. %s", core.ResourceRegionFallbackDocstring), core.Resource), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: descriptions["id"], + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "project_id": schema.StringAttribute{ + Description: descriptions["project_id"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + validate.UUID(), + validate.NoSeparator(), + }, + }, + "region": schema.StringAttribute{ + Description: descriptions["region"], + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "name": schema.StringAttribute{ + Description: descriptions["name"], + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^[0-9a-z](?:(?:[0-9a-z]|-){0,61}[0-9a-z])?$`), + "must start and end with an alphanumeric character, may contain hyphens, and be 1-63 characters long", + ), + }, + }, + "rules": schema.ListNestedAttribute{ + Description: descriptions["rules"], + Required: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "behaviour": schema.SingleNestedAttribute{ + Description: descriptions["behaviour"], + Required: true, + Attributes: map[string]schema.Attribute{ + "action": schema.StringAttribute{ + Description: descriptions["behaviour_action"], + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf(actionOptions...), + }, + }, + "log": schema.BoolAttribute{ + Description: descriptions["behaviour_log"], + Optional: true, + }, + "log_msg": schema.StringAttribute{ + Description: descriptions["behaviour_log_msg"], + Optional: true, + }, + "severity": schema.StringAttribute{ + Description: descriptions["behaviour_severity"], + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + "conditions": schema.ListNestedAttribute{ + Description: descriptions["rule_conditions"], + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "operator": schema.SingleNestedAttribute{ + Description: descriptions["operator"], + Required: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Description: descriptions["operator_type"], + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf(operatorTypeOptions...), + }, + }, + "value": schema.StringAttribute{ + Description: descriptions["operator_value"], + Optional: true, + }, + }, + }, + "transformations": schema.ListAttribute{ + Description: descriptions["transformations"], + Optional: true, + ElementType: types.StringType, + Validators: []validator.List{ + listvalidator.ValueStringsAre( + stringvalidator.OneOf(transformationOptions...), + ), + }, + }, + "variable": schema.SingleNestedAttribute{ + Description: descriptions["variable"], + Required: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + Description: descriptions["variable_type"], + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf(variableTypeOptions...), + }, + }, + "value": schema.StringAttribute{ + Description: descriptions["variable_value"], + Optional: true, + }, + }, + }, + }, + }, + }, + "description": schema.StringAttribute{ + Description: descriptions["rule_description"], + Optional: true, + }, + "id": schema.Int32Attribute{ + Description: descriptions["rule_id"], + Computed: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + "usage": schema.SingleNestedAttribute{ + Description: descriptions["usage"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "count": schema.Int32Attribute{ + Description: descriptions["usage_count"], + Computed: true, + }, + "items": schema.ListAttribute{ + Description: descriptions["usage_items"], + Computed: true, + ElementType: types.StringType, + }, + }, + }, + }, + } +} + +func (r *customRuleGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform + var configModel Model + if req.Config.Raw.IsNull() { + return + } + resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...) + if resp.Diagnostics.HasError() { + return + } + + var planModel Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...) + if resp.Diagnostics.HasError() { + return + } + + tfutils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *customRuleGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idParts := strings.Split(req.ID, core.Separator) + + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + core.LogAndAddError(ctx, &resp.Diagnostics, + "Error importing ALB WAF Custom Rule Group", + fmt.Sprintf("Expected import identifier with format: [project_id],[region],[name] Got: %q", req.ID), + ) + return + } + + ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ + "project_id": idParts[0], + "region": idParts[1], + "name": idParts[2], + }) + tflog.Info(ctx, "ALB WAF Custom Rule Group state imported") +} + +func (r *customRuleGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.Plan.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "name", model.Name) + + payload, err := toCreatePayload(ctx, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Custom Rule Group", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + createResp, err := r.client.DefaultAPI.CreateCustomRuleGroup(ctx, projectId, region).CreateCustomRuleGroupPayload(*payload).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Custom Rule Group", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + if createResp.Name == nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Custom Rule Group", "Got empty Custom Rule Group name") + return + } + customRuleGroupName := *createResp.Name + + ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ + "project_id": projectId, + "region": region, + "name": customRuleGroupName, + }) + if resp.Diagnostics.HasError() { + return + } + + err = mapFields(ctx, createResp, &model, region) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Custom Rule Group", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "ALB WAF Custom Rule Group created") +} + +func (r *customRuleGroupResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform + core.LogAndAddError(ctx, &resp.Diagnostics, "Ressource not updatable", "ALB WAF Custom Rule Group is not updatable") +} + +func (r *customRuleGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + name := model.Name.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "name", name) + + customRuleGroupResp, err := r.client.DefaultAPI.GetCustomRuleGroup(ctx, projectId, region, name).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading ALB WAF Custom Rule Group", err.Error()) + return + } + + ctx = core.LogResponse(ctx) + + err = mapFields(ctx, customRuleGroupResp, &model, region) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading ALB WAF Custom Rule Group", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, "ALB WAF Custom Rule Group read") +} + +func (r *customRuleGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + name := model.Name.ValueString() + region := r.providerData.GetRegionWithOverride(model.Region) + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + ctx = tflog.SetField(ctx, "name", name) + + _, err := r.client.DefaultAPI.DeleteCustomRuleGroup(ctx, projectId, region, name).Execute() + if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + tflog.Info(ctx, "ALB WAF Custom Rule Group was already deleted") + return + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting ALB WAF Custom Rule Group", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + tflog.Info(ctx, "ALB WAF Custom Rule Group deleted") +} + +func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRuleGroupPayload, error) { + if model == nil { + return nil, fmt.Errorf("nil model") + } + + payloadRules := []albWaf.CreateCustomRule{} + if !tfutils.IsUndefined(model.Rules) { + rules := []RuleModel{} + diags := model.Rules.ElementsAs(ctx, &rules, true) + if diags.HasError() { + return nil, fmt.Errorf("converting to rule map: %v", diags.Errors()) + } + + for _, rule := range rules { + behaviour := BehaviourModel{} + if !tfutils.IsUndefined(rule.Behaviour) { + diags := rule.Behaviour.As(ctx, &behaviour, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting to rule behaviour: %v", diags.Errors()) + } + } + + conditions, err := toConditionsPayload(ctx, rule.Conditions) + if err != nil || conditions == nil { + return nil, fmt.Errorf("converting conditions: %v", err) + } + + payloadRules = append(payloadRules, albWaf.CreateCustomRule{ + Behaviour: &albWaf.Behaviour{ + Action: (*albWaf.BehaviourAction)(behaviour.Action.ValueStringPointer()), + Log: behaviour.Log.ValueBoolPointer(), + LogMsg: behaviour.LogMsg.ValueStringPointer(), + }, + Conditions: *conditions, + Description: rule.Description.ValueStringPointer(), + }) + } + } + + payload := &albWaf.CreateCustomRuleGroupPayload{ + Name: model.Name.ValueStringPointer(), + Rules: payloadRules, + } + + return payload, nil +} + +func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (*[]albWaf.Condition, error) { + result := []albWaf.Condition{} + + if !tfutils.IsUndefined(conditions) { + conditionModels := []ConditionModel{} + diags := conditions.ElementsAs(ctx, &conditionModels, true) + if diags.HasError() { + return nil, fmt.Errorf("converting to rule map: %v", diags.Errors()) + } + + for _, condition := range conditionModels { + transformations := []albWaf.ConditionTransformationsInner{} + if !tfutils.IsUndefined(condition.Transformations) { + diags := condition.Transformations.ElementsAs(ctx, &transformations, true) + if diags.HasError() { + return nil, fmt.Errorf("converting transformations: %v", diags.Errors()) + } + } + + var operator *albWaf.ConditionOperator + var operatorModel = OperatorModel{} + if !tfutils.IsUndefined(condition.Operator) { + diags = condition.Operator.As(ctx, &operatorModel, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting operator: %v", diags.Errors()) + } + + operator = &albWaf.ConditionOperator{ + Type: (*albWaf.ConditionOperatorType)(operatorModel.Type.ValueStringPointer()), + Value: operatorModel.Value.ValueStringPointer(), + } + } + + var variable *albWaf.ConditionVariable + var variableModel = VariableModel{} + if !tfutils.IsUndefined(condition.Variable) { + diags = condition.Variable.As(ctx, &variableModel, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting variable: %v", diags.Errors()) + } + + variable = &albWaf.ConditionVariable{ + Type: (*albWaf.ConditionVariableType)(variableModel.Type.ValueStringPointer()), + Value: variableModel.Value.ValueStringPointer(), + } + } + + result = append(result, albWaf.Condition{ + Operator: operator, + Transformations: transformations, + Variable: variable, + }) + } + } + + return &result, nil +} + +func mapFields(ctx context.Context, customRuleGroup *albWaf.GetCustomRuleGroupResponse, model *Model, region string) error { + if customRuleGroup == nil { + return fmt.Errorf("response input is nil") + } + if model == nil { + return fmt.Errorf("model input is nil") + } + + model.Id = tfutils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, model.Name.ValueString()) + model.Name = types.StringValue(model.Name.ValueString()) + model.Region = types.StringValue(region) + + rules, err := mapRules(ctx, &customRuleGroup.Rules) + if err != nil || rules == nil { + return fmt.Errorf("map rules: %w", err) + } + model.Rules = *rules + + usage, err := mapUsage(ctx, customRuleGroup.Usage) + if err != nil || usage == nil { + return fmt.Errorf("map usage: %w", err) + } + model.Usage = *usage + + return nil +} + +func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.ListValue, error) { + var diags diag.Diagnostics + var result basetypes.ListValue + + if rules != nil { + rulesList := []attr.Value{} + for _, rule := range *rules { + ruleTF := RuleModel{ + Id: types.Int32PointerValue(rule.Id), + Description: types.StringPointerValue(rule.Description), + } + + behaviour, err := mapBehaviour(ctx, rule.Behaviour) + if err != nil || behaviour == nil { + return nil, fmt.Errorf("map behaviour: %w", err) + } + ruleTF.Behaviour = *behaviour + + conditions, err := mapConditions(ctx, rule) + if err != nil || conditions == nil { + return nil, fmt.Errorf("map conditions: %w", err) + } + ruleTF.Conditions = *conditions + + rule, diags := types.ObjectValueFrom(ctx, ruleType, ruleTF) + if diags.HasError() { + return nil, fmt.Errorf("mapping rule: %w", core.DiagsToError(diags)) + } + rulesList = append(rulesList, rule) + } + result, diags = types.ListValue(types.ObjectType{AttrTypes: ruleType}, rulesList) + if diags.HasError() { + return nil, fmt.Errorf("creating rule object: %w", core.DiagsToError(diags)) + } + } else { + result = types.ListNull(types.ObjectType{AttrTypes: ruleType}) + } + + return &result, nil +} + +func mapBehaviour(ctx context.Context, behaviour *albWaf.GetBehaviour) (*basetypes.ObjectValue, error) { + var diags diag.Diagnostics + var result basetypes.ObjectValue + + if behaviour != nil { + behaviourModel := BehaviourModel{ + Action: types.StringPointerValue((*string)(behaviour.Action)), + Log: types.BoolPointerValue(behaviour.Log), + LogMsg: types.StringPointerValue(behaviour.LogMsg), + Severity: types.StringPointerValue((*string)(behaviour.Severity)), + } + + result, diags = types.ObjectValueFrom(ctx, behaviourType, behaviourModel) + if diags.HasError() { + return nil, fmt.Errorf("creating behaviour object: %w", core.DiagsToError(diags)) + } + } else { + result = types.ObjectNull(behaviourType) + } + + return &result, nil +} + +func mapConditions(ctx context.Context, rule albWaf.GetCustomRule) (*basetypes.ListValue, error) { + var diags diag.Diagnostics + var result basetypes.ListValue + + if conditions, ok := rule.GetConditionsOk(); ok { + conditionsList := []attr.Value{} + for _, condition := range conditions { + conditionTF := ConditionModel{} + + if operator, ok := condition.GetOperatorOk(); ok { + operatorModel := OperatorModel{ + Type: types.StringPointerValue((*string)(operator.Type)), + Value: types.StringPointerValue(operator.Value), + } + + conditionTF.Operator, diags = types.ObjectValueFrom(ctx, operatorType, operatorModel) + if diags.HasError() { + return nil, fmt.Errorf("creating operator object: %w", core.DiagsToError(diags)) + } + } else { + conditionTF.Operator = types.ObjectNull(operatorType) + } + + conditionTF.Transformations, diags = types.ListValueFrom(ctx, types.StringType, condition.Transformations) + if diags.HasError() { + return nil, fmt.Errorf("mapping transformations: %w", core.DiagsToError(diags)) + } + + if variable, ok := condition.GetVariableOk(); ok { + variableModel := VariableModel{ + Type: types.StringPointerValue((*string)(variable.Type)), + Value: types.StringPointerValue(variable.Value), + } + + conditionTF.Variable, diags = types.ObjectValueFrom(ctx, variableType, variableModel) + if diags.HasError() { + return nil, fmt.Errorf("creating variable object: %w", core.DiagsToError(diags)) + } + } else { + conditionTF.Variable = types.ObjectNull(variableType) + } + + condition, diags := types.ObjectValueFrom(ctx, conditionType, conditionTF) + if diags.HasError() { + return nil, fmt.Errorf("mapping condition: %w", core.DiagsToError(diags)) + } + conditionsList = append(conditionsList, condition) + } + result, diags = types.ListValue(types.ObjectType{AttrTypes: conditionType}, conditionsList) + if diags.HasError() { + return nil, fmt.Errorf("mapping conditions: %w", core.DiagsToError(diags)) + } + } else { + result = types.ListNull(types.ObjectType{AttrTypes: conditionType}) + } + + return &result, nil +} + +func mapUsage(ctx context.Context, usage *albWaf.CRGUsage) (*basetypes.ObjectValue, error) { + var diags diag.Diagnostics + var result basetypes.ObjectValue + + if usage != nil { + usageModel := UsageModel{ + Count: types.Int32PointerValue(usage.Count), + } + + usageModel.Items, diags = types.ListValueFrom(ctx, types.StringType, usage.GetItems()) + if diags.HasError() { + return nil, fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) + } + + result, diags = types.ObjectValueFrom(ctx, usageType, usageModel) + if diags.HasError() { + return nil, fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) + } + } else { + result = types.ObjectNull(usageType) + } + + return &result, nil +} diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go new file mode 100644 index 000000000..4179cb981 --- /dev/null +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -0,0 +1,344 @@ +package custom_rule_group + +import ( + "context" + _ "embed" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" +) + +var ( + testProjectId = types.StringValue(uuid.NewString()) + testRegion = types.StringValue("eu01") + testName = types.StringValue("test-custom-rule-group") + testId = types.StringValue(testProjectId.ValueString() + "," + testRegion.ValueString() + "," + testName.ValueString()) +) + +func TestToCreatePayload(t *testing.T) { + tests := []struct { + name string + model *Model + expected *albWaf.CreateCustomRuleGroupPayload + isValid bool + }{ + { + name: "default", + model: &Model{ + Name: testName, + Id: testId, + ProjectId: testProjectId, + Region: testRegion, + Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ + types.ObjectValueMust(ruleType, map[string]attr.Value{ + "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "action": types.StringValue("some-action"), + "log": types.BoolValue(true), + "log_msg": types.StringValue("Log: something happened"), + "severity": types.StringNull(), + }), + "conditions": types.ListValueMust(types.ObjectType{AttrTypes: conditionType}, []attr.Value{ + types.ObjectValueMust(conditionType, map[string]attr.Value{ + "operator": types.ObjectValueMust(operatorType, map[string]attr.Value{ + "type": types.StringValue("operator-type"), + "value": types.StringValue("operator-value"), + }), + "transformations": types.ListValueMust(types.StringType, []attr.Value{ + types.StringValue("foo"), + types.StringValue("bar"), + }), + "variable": types.ObjectValueMust(variableType, map[string]attr.Value{ + "type": types.StringValue("variable-type"), + "value": types.StringValue("variable-value"), + }), + }), + }), + "description": types.StringValue("foo-bar"), + "id": types.Int32Null(), + }), + }), + }, + expected: &albWaf.CreateCustomRuleGroupPayload{ + Name: testName.ValueStringPointer(), + Rules: []albWaf.CreateCustomRule{ + albWaf.CreateCustomRule{ + Behaviour: &albWaf.Behaviour{ + Action: new(albWaf.BehaviourAction("some-action")), + Log: new(true), + LogMsg: new("Log: something happened"), + }, + Conditions: []albWaf.Condition{ + albWaf.Condition{ + Operator: &albWaf.ConditionOperator{ + Type: new(albWaf.ConditionOperatorType("operator-type")), + Value: new("operator-value"), + }, + Transformations: []albWaf.ConditionTransformationsInner{ + "foo", + "bar", + }, + Variable: &albWaf.ConditionVariable{ + Type: new(albWaf.ConditionVariableType("variable-type")), + Value: new("variable-value"), + }, + }, + }, + Description: new("foo-bar"), + }, + }, + }, + isValid: true, + }, + { + name: "null values", + model: &Model{ + Name: testName, + Id: testId, + ProjectId: testProjectId, + Region: testRegion, + Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ + types.ObjectValueMust(ruleType, map[string]attr.Value{ + "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "action": types.StringNull(), + "log": types.BoolNull(), + "log_msg": types.StringNull(), + "severity": types.StringNull(), + }), + "conditions": types.ListValueMust(types.ObjectType{AttrTypes: conditionType}, []attr.Value{ + types.ObjectValueMust(conditionType, map[string]attr.Value{ + "operator": types.ObjectValueMust(operatorType, map[string]attr.Value{ + "type": types.StringNull(), + "value": types.StringNull(), + }), + "transformations": types.ListValueMust(types.StringType, []attr.Value{}), + "variable": types.ObjectValueMust(variableType, map[string]attr.Value{ + "type": types.StringNull(), + "value": types.StringNull(), + }), + }), + }), + "description": types.StringNull(), + "id": types.Int32Null(), + }), + }), + }, + expected: &albWaf.CreateCustomRuleGroupPayload{ + Name: testName.ValueStringPointer(), + Rules: []albWaf.CreateCustomRule{ + albWaf.CreateCustomRule{ + Behaviour: &albWaf.Behaviour{}, + Conditions: []albWaf.Condition{ + albWaf.Condition{ + Operator: &albWaf.ConditionOperator{}, + Transformations: []albWaf.ConditionTransformationsInner{}, + Variable: &albWaf.ConditionVariable{}, + }, + }, + }, + }, + }, + isValid: true, + }, + { + name: "no rules", + model: &Model{ + Name: testName, + Id: testId, + ProjectId: testProjectId, + Region: testRegion, + }, + expected: &albWaf.CreateCustomRuleGroupPayload{ + Name: testName.ValueStringPointer(), + Rules: []albWaf.CreateCustomRule{}, + }, + isValid: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := toCreatePayload(context.Background(), tt.model) + if (err != nil) == tt.isValid { + t.Errorf("toCreatePayload() error = %v, isValid %v", err, tt.isValid) + return + } + + if tt.isValid { + if diff := cmp.Diff(got, tt.expected); diff != "" { + t.Errorf("Data does not match: %s", diff) + } + } + }) + } +} + +func TestMapFields(t *testing.T) { + tests := []struct { + name string + state *Model + region string + input *albWaf.GetCustomRuleGroupResponse + expected *Model + isValid bool + }{ + { + name: "default", + state: &Model{ + ProjectId: testProjectId, + Region: testRegion, + Name: testName, + Id: testId, + Rules: types.ListNull(types.ObjectType{AttrTypes: ruleType}), + }, + region: testRegion.ValueString(), + input: &albWaf.GetCustomRuleGroupResponse{ + Name: testName.ValueStringPointer(), + Rules: []albWaf.GetCustomRule{ + albWaf.GetCustomRule{ + Behaviour: &albWaf.GetBehaviour{ + Action: new(albWaf.GetBehaviourAction("some-action")), + Log: new(true), + LogMsg: new("Log: something happened"), + Severity: new(albWaf.GetBehaviourSeverity("critical")), + }, + Conditions: []albWaf.Condition{ + albWaf.Condition{ + Operator: &albWaf.ConditionOperator{ + Type: new(albWaf.ConditionOperatorType("operator-type")), + Value: new("operator-value"), + }, + Transformations: []albWaf.ConditionTransformationsInner{ + "foo", + "bar", + }, + Variable: &albWaf.ConditionVariable{ + Type: new(albWaf.ConditionVariableType("variable-type")), + Value: new("variable-value"), + }, + }, + }, + Description: new("foo-bar"), + Id: new(int32(42)), + }, + }, + Usage: &albWaf.CRGUsage{ + Count: new(int32(42)), + Items: []string{ + "one", + "two", + "three", + }, + }, + }, + expected: &Model{ + ProjectId: testProjectId, + Region: testRegion, + Name: testName, + Id: testId, + Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ + types.ObjectValueMust(ruleType, map[string]attr.Value{ + "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "action": types.StringValue("some-action"), + "log": types.BoolValue(true), + "log_msg": types.StringValue("Log: something happened"), + "severity": types.StringValue("critical"), + }), + "conditions": types.ListValueMust(types.ObjectType{AttrTypes: conditionType}, []attr.Value{ + types.ObjectValueMust(conditionType, map[string]attr.Value{ + "operator": types.ObjectValueMust(operatorType, map[string]attr.Value{ + "type": types.StringValue("operator-type"), + "value": types.StringValue("operator-value"), + }), + "transformations": types.ListValueMust(types.StringType, []attr.Value{ + types.StringValue("foo"), + types.StringValue("bar"), + }), + "variable": types.ObjectValueMust(variableType, map[string]attr.Value{ + "type": types.StringValue("variable-type"), + "value": types.StringValue("variable-value"), + }), + }), + }), + "description": types.StringValue("foo-bar"), + "id": types.Int32Value(42), + }), + }), + Usage: types.ObjectValueMust(usageType, map[string]attr.Value{ + "count": types.Int32Value(42), + "items": types.ListValueMust(types.StringType, []attr.Value{ + types.StringValue("one"), + types.StringValue("two"), + types.StringValue("three"), + }), + }), + }, + isValid: true, + }, + { + name: "empty rule", + state: &Model{ + ProjectId: testProjectId, + Region: testRegion, + Name: testName, + Id: testId, + Rules: types.ListNull(types.ObjectType{AttrTypes: ruleType}), + }, + region: testRegion.ValueString(), + input: &albWaf.GetCustomRuleGroupResponse{ + Rules: []albWaf.GetCustomRule{ + albWaf.GetCustomRule{}, + }, + }, + expected: &Model{ + Name: testName, + Id: testId, + ProjectId: testProjectId, + Region: testRegion, + Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ + types.ObjectValueMust(ruleType, map[string]attr.Value{ + "behaviour": types.ObjectNull(behaviourType), + "conditions": types.ListNull(types.ObjectType{AttrTypes: conditionType}), + "description": types.StringNull(), + "id": types.Int32Null(), + }), + }), + }, + isValid: true, + }, + { + name: "no rules", + state: &Model{ + ProjectId: testProjectId, + Region: testRegion, + Name: testName, + Id: testId, + }, + region: testRegion.ValueString(), + input: &albWaf.GetCustomRuleGroupResponse{}, + expected: &Model{ + Name: testName, + Id: testId, + ProjectId: testProjectId, + Region: testRegion, + Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{}), + }, + isValid: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + if err := mapFields(ctx, tt.input, tt.state, tt.region); (err == nil) != tt.isValid { + t.Errorf("unexpected error") + } + if tt.isValid { + if diff := cmp.Diff(tt.state, tt.expected); diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }) + } +} diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource.go b/stackit/internal/services/albwaf/managed_rule_set/resource.go index 0409ec6e8..a63718da0 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource.go @@ -475,13 +475,13 @@ func mapFields(ctx context.Context, managedRuleSet *albWaf.GetManagedRuleSetResp ruleMap[ruleKey], diags = types.ObjectValueFrom(ctx, ruleType, ruleTF) if diags.HasError() { - return fmt.Errorf("mapping role: %w", core.DiagsToError(diags)) + return fmt.Errorf("mapping rule: %w", core.DiagsToError(diags)) } } } groupTF.Rules, diags = types.MapValue(types.ObjectType{AttrTypes: ruleType}, ruleMap) if diags.HasError() { - return fmt.Errorf("mapping roles: %w", core.DiagsToError(diags)) + return fmt.Errorf("mapping rules: %w", core.DiagsToError(diags)) } groupsMap[groupKey], diags = types.ObjectValueFrom(ctx, ruleGroupType, groupTF) diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf new file mode 100644 index 000000000..bf81b4d9d --- /dev/null +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf @@ -0,0 +1,42 @@ + +variable "project_id" {} +variable "name" {} +variable "description" {} +variable "action" {} +variable "log" {} +variable "log_msg" {} +variable "operator_type" {} +variable "operator_value" {} +variable "transformation" {} +variable "variable_type" {} +variable "variable_value" {} + +resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { + project_id = var.project_id + name = var.name + rules = [ + { + description = var.description + behaviour = { + action = var.action + log = var.log + logMsg = var.log_msg + } + conditions = [ + { + operator = { + type = var.operator_type + value = var.operator_value + } + transformations = [ + var.transformation + ] + variable = { + type = var.variable_type + value = var.variable_value + } + } + ] + } + ] +} diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf new file mode 100644 index 000000000..10dd562be --- /dev/null +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf @@ -0,0 +1,29 @@ + +variable "project_id" {} +variable "name" {} +variable "action" {} +variable "operator_type" {} +variable "variable_type" {} + +resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { + project_id = var.project_id + name = var.name + rules = [ + { + behaviour = { + action = var.action + } + conditions = [ + { + operator = { + type = var.operator_type + value = "dummy" + } + variable = { + type = var.variable_type + } + } + ] + } + ] +} diff --git a/stackit/provider.go b/stackit/provider.go index 5c99aaa50..41008bc20 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -21,6 +21,7 @@ import ( "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/access_token" alb "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/alb/applicationloadbalancer" cert "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/albcertificates/certificate" + albWafCustomRuleGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/albwaf/custom_rule_group" albWafManagedRuleSet "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/albwaf/managed_rule_set" customRole "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/authorization/customrole" roleAssignements "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/authorization/roleassignments" @@ -664,6 +665,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource { dataSources := []func() datasource.DataSource{ alb.NewApplicationLoadBalancerDataSource, + albWafCustomRuleGroup.NewCustomRuleGroupDataSource, albWafManagedRuleSet.NewManagedRuleSetDataSource, alertGroup.NewAlertGroupDataSource, cdn.NewDistributionDataSource, @@ -779,6 +781,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource func (p *Provider) Resources(_ context.Context) []func() resource.Resource { resources := []func() resource.Resource{ alb.NewApplicationLoadBalancerResource, + albWafCustomRuleGroup.NewCustomRuleGroupResource, albWafManagedRuleSet.NewManagedRuleSetResource, alertGroup.NewAlertGroupResource, cdn.NewDistributionResource, From f6bb83d8db89eb91710fa761ef27c6d7a3336c7c Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 27 Jul 2026 18:21:38 +0200 Subject: [PATCH 02/17] generate-docu and format --- .../data-sources/alb_waf_custom_rule_group.md | 92 ++++++++++++++ docs/resources/alb_waf_custom_rule_group.md | 113 ++++++++++++++++++ .../albwaf/custom_rule_group/resource.go | 2 +- .../albwaf/custom_rule_group/resource_test.go | 14 +-- .../albwaf/testdata/custom-rule-group-max.tf | 4 +- .../albwaf/testdata/custom-rule-group-min.tf | 2 +- 6 files changed, 216 insertions(+), 11 deletions(-) create mode 100644 docs/data-sources/alb_waf_custom_rule_group.md create mode 100644 docs/resources/alb_waf_custom_rule_group.md diff --git a/docs/data-sources/alb_waf_custom_rule_group.md b/docs/data-sources/alb_waf_custom_rule_group.md new file mode 100644 index 000000000..0ed80c49e --- /dev/null +++ b/docs/data-sources/alb_waf_custom_rule_group.md @@ -0,0 +1,92 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_alb_waf_custom_rule_group Data Source - stackit" +subcategory: "" +description: |- + ALB WAF Custom Rule Group resource schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on resource level. + ~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources. +--- + +# stackit_alb_waf_custom_rule_group (Data Source) + +ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level. + +~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. + + + + +## Schema + +### Required + +- `name` (String) Custom rule group configuration name. +- `project_id` (String) STACKIT project ID associated with the ALB WAF Custom Rule Group. + +### Optional + +- `region` (String) STACKIT region name the resource is located in. If not defined, the provider region is used. + +### Read-Only + +- `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". +- `rules` (Attributes List) Enriched rules containing auto-generated IDs and computed severity values. (see [below for nested schema](#nestedatt--rules)) +- `usage` (Attributes) Tracking metrics for CRG resource utilization. (see [below for nested schema](#nestedatt--usage)) + + +### Nested Schema for `rules` + +Read-Only: + +- `behaviour` (Attributes) (see [below for nested schema](#nestedatt--rules--behaviour)) +- `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) +- `description` (String) A clear description explaining the threat vector or criteria addressed by this rule. +- `id` (Number) Backend auto-allocated unique rule ID within the valid 1-99999 threshold. + + +### Nested Schema for `rules.behaviour` + +Read-Only: + +- `action` (String) The protective stance action. ACTION_DENY forces a 403 status response code. +- `log` (Boolean) Determines whether an entry should be generated in the security ledger upon a rule hit. +- `log_msg` (String) Custom notification message string mapped to underlying logdata contexts. Required if log is true. +- `severity` (String) Severity classification metric used by internal analytics graphs. + + + +### Nested Schema for `rules.conditions` + +Read-Only: + +- `operator` (Attributes) The comparison logic executed against the transformed variable. (see [below for nested schema](#nestedatt--rules--conditions--operator)) +- `transformations` (List of String) Ordered normalization steps applied before the operator runs. +- `variable` (Attributes) The part of the HTTP transaction to inspect. (see [below for nested schema](#nestedatt--rules--conditions--variable)) + + +### Nested Schema for `rules.conditions.operator` + +Read-Only: + +- `type` (String) The operational evaluation type definition macro. +- `value` (String) The text or rule regex pattern arguments applied inside the operator execution loop. + + + +### Nested Schema for `rules.conditions.variable` + +Read-Only: + +- `type` (String) The targeted validation engine variable macro. +- `value` (String) Optional key element context for map variables (e.g., matching a 'Host' header key). + + + + + +### Nested Schema for `usage` + +Read-Only: + +- `count` (Number) Number of WAF configurations actively using this rule group. +- `items` (List of String) List of individual WAF configuration names that bind this rule group. diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md new file mode 100644 index 000000000..b265241ad --- /dev/null +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -0,0 +1,113 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_alb_waf_custom_rule_group Resource - stackit" +subcategory: "" +description: |- + ALB WAF Custom Rule Group resource schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on resource level. + ~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources. +--- + +# stackit_alb_waf_custom_rule_group (Resource) + +ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level. + +~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. + + + + +## Schema + +### Required + +- `name` (String) Custom rule group configuration name. +- `project_id` (String) STACKIT project ID associated with the ALB WAF Custom Rule Group. +- `rules` (Attributes List) Enriched rules containing auto-generated IDs and computed severity values. (see [below for nested schema](#nestedatt--rules)) + +### Optional + +- `region` (String) STACKIT region name the resource is located in. If not defined, the provider region is used. + +### Read-Only + +- `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". +- `usage` (Attributes) Tracking metrics for CRG resource utilization. (see [below for nested schema](#nestedatt--usage)) + + +### Nested Schema for `rules` + +Required: + +- `behaviour` (Attributes) (see [below for nested schema](#nestedatt--rules--behaviour)) + +Optional: + +- `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) +- `description` (String) A clear description explaining the threat vector or criteria addressed by this rule. + +Read-Only: + +- `id` (Number) Backend auto-allocated unique rule ID within the valid 1-99999 threshold. + + +### Nested Schema for `rules.behaviour` + +Required: + +- `action` (String) The protective stance action. ACTION_DENY forces a 403 status response code. + +Optional: + +- `log` (Boolean) Determines whether an entry should be generated in the security ledger upon a rule hit. +- `log_msg` (String) Custom notification message string mapped to underlying logdata contexts. Required if log is true. + +Read-Only: + +- `severity` (String) Severity classification metric used by internal analytics graphs. + + + +### Nested Schema for `rules.conditions` + +Required: + +- `operator` (Attributes) The comparison logic executed against the transformed variable. (see [below for nested schema](#nestedatt--rules--conditions--operator)) +- `variable` (Attributes) The part of the HTTP transaction to inspect. (see [below for nested schema](#nestedatt--rules--conditions--variable)) + +Optional: + +- `transformations` (List of String) Ordered normalization steps applied before the operator runs. + + +### Nested Schema for `rules.conditions.operator` + +Required: + +- `type` (String) The operational evaluation type definition macro. + +Optional: + +- `value` (String) The text or rule regex pattern arguments applied inside the operator execution loop. + + + +### Nested Schema for `rules.conditions.variable` + +Required: + +- `type` (String) The targeted validation engine variable macro. + +Optional: + +- `value` (String) Optional key element context for map variables (e.g., matching a 'Host' header key). + + + + + +### Nested Schema for `usage` + +Read-Only: + +- `count` (Number) Number of WAF configurations actively using this rule group. +- `items` (List of String) List of individual WAF configuration names that bind this rule group. diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 7479b63bd..7841808fe 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -565,7 +565,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul conditions, err := toConditionsPayload(ctx, rule.Conditions) if err != nil || conditions == nil { - return nil, fmt.Errorf("converting conditions: %v", err) + return nil, fmt.Errorf("converting conditions: %w", err) } payloadRules = append(payloadRules, albWaf.CreateCustomRule{ diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 4179cb981..20474765b 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -65,14 +65,14 @@ func TestToCreatePayload(t *testing.T) { expected: &albWaf.CreateCustomRuleGroupPayload{ Name: testName.ValueStringPointer(), Rules: []albWaf.CreateCustomRule{ - albWaf.CreateCustomRule{ + { Behaviour: &albWaf.Behaviour{ Action: new(albWaf.BehaviourAction("some-action")), Log: new(true), LogMsg: new("Log: something happened"), }, Conditions: []albWaf.Condition{ - albWaf.Condition{ + { Operator: &albWaf.ConditionOperator{ Type: new(albWaf.ConditionOperatorType("operator-type")), Value: new("operator-value"), @@ -129,10 +129,10 @@ func TestToCreatePayload(t *testing.T) { expected: &albWaf.CreateCustomRuleGroupPayload{ Name: testName.ValueStringPointer(), Rules: []albWaf.CreateCustomRule{ - albWaf.CreateCustomRule{ + { Behaviour: &albWaf.Behaviour{}, Conditions: []albWaf.Condition{ - albWaf.Condition{ + { Operator: &albWaf.ConditionOperator{}, Transformations: []albWaf.ConditionTransformationsInner{}, Variable: &albWaf.ConditionVariable{}, @@ -197,7 +197,7 @@ func TestMapFields(t *testing.T) { input: &albWaf.GetCustomRuleGroupResponse{ Name: testName.ValueStringPointer(), Rules: []albWaf.GetCustomRule{ - albWaf.GetCustomRule{ + { Behaviour: &albWaf.GetBehaviour{ Action: new(albWaf.GetBehaviourAction("some-action")), Log: new(true), @@ -205,7 +205,7 @@ func TestMapFields(t *testing.T) { Severity: new(albWaf.GetBehaviourSeverity("critical")), }, Conditions: []albWaf.Condition{ - albWaf.Condition{ + { Operator: &albWaf.ConditionOperator{ Type: new(albWaf.ConditionOperatorType("operator-type")), Value: new("operator-value"), @@ -289,7 +289,7 @@ func TestMapFields(t *testing.T) { region: testRegion.ValueString(), input: &albWaf.GetCustomRuleGroupResponse{ Rules: []albWaf.GetCustomRule{ - albWaf.GetCustomRule{}, + {}, }, }, expected: &Model{ diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf index bf81b4d9d..6f6093981 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf @@ -12,8 +12,8 @@ variable "variable_type" {} variable "variable_value" {} resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { - project_id = var.project_id - name = var.name + project_id = var.project_id + name = var.name rules = [ { description = var.description diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf index 10dd562be..29e7a71db 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf @@ -20,7 +20,7 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { value = "dummy" } variable = { - type = var.variable_type + type = var.variable_type } } ] From 5f3c1b0b07f8c36595d82bbf4bfba96f73f9327d Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 27 Jul 2026 19:51:33 +0200 Subject: [PATCH 03/17] upgrade to albwaf sdk v0.11.0 --- docs/resources/alb_waf_custom_rule_group.md | 2 +- go.mod | 2 +- go.sum | 4 +- .../services/albwaf/albwaf_acc_test.go | 6 +-- .../albwaf/custom_rule_group/resource.go | 50 ++++++++----------- .../albwaf/custom_rule_group/resource_test.go | 32 ++++++------ .../albwaf/managed_rule_set/resource.go | 4 +- .../albwaf/managed_rule_set/resource_test.go | 4 +- 8 files changed, 47 insertions(+), 57 deletions(-) diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index b265241ad..41e778dc8 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -39,10 +39,10 @@ ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified i Required: - `behaviour` (Attributes) (see [below for nested schema](#nestedatt--rules--behaviour)) +- `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) Optional: -- `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) - `description` (String) A clear description explaining the threat vector or criteria addressed by this rule. Read-Only: diff --git a/go.mod b/go.mod index 0645f5ff4..b870f2e66 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/stackitcloud/stackit-sdk-go/core v0.26.0 github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 - github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.10.0 + github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0 github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 github.com/stackitcloud/stackit-sdk-go/services/certificates v1.9.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 diff --git a/go.sum b/go.sum index 5f4b12852..3accd98a5 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10 github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 h1:WoWlHdzISGXPEaJOYt6HP5F9M5nbyCJL6VqRJZIaOQs= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0/go.mod h1:eK6oRB5Tmpt6KbXQ4UYBGg2LgW5bPtVoncL9E8JSRww= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.10.0 h1:0WsTSSZ0LjNpM3E1d3MgkBXmzMQThVQ7IuXhL2w4EyM= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.10.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0 h1:ejTZTnGKFUWs9Ch9U30Jd+tpDA/SnHuSF9DpfD6w+To= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY= github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 h1:k+KJ4gp9awhJMY5y55vDqRSr6G/S9+8haTNILGbgH9s= diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 3de990be9..8544e5f0f 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/stackitcloud/stackit-sdk-go/core/utils" - albwaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" @@ -441,8 +441,8 @@ func TestAccManagedRuleSet(t *testing.T) { }) } -func createClient() (*albwaf.APIClient, error) { - client, err := albwaf.NewAPIClient(testutil.NewConfigBuilder().BuildClientOptions(testutil.AlbWafCustomEndpoint, false)...) +func createClient() (*albWaf.APIClient, error) { + client, err := albWaf.NewAPIClient(testutil.NewConfigBuilder().BuildClientOptions(testutil.AlbWafCustomEndpoint, false)...) if err != nil { return nil, fmt.Errorf("creating client: %w", err) } diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 7841808fe..d49b422cd 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -272,7 +272,7 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq }, "conditions": schema.ListNestedAttribute{ Description: descriptions["rule_conditions"], - Optional: true, + Required: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "operator": schema.SingleNestedAttribute{ @@ -569,8 +569,8 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul } payloadRules = append(payloadRules, albWaf.CreateCustomRule{ - Behaviour: &albWaf.Behaviour{ - Action: (*albWaf.BehaviourAction)(behaviour.Action.ValueStringPointer()), + Behaviour: albWaf.Behaviour{ + Action: albWaf.BehaviourAction(behaviour.Action.ValueString()), Log: behaviour.Log.ValueBoolPointer(), LogMsg: behaviour.LogMsg.ValueStringPointer(), }, @@ -581,7 +581,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul } payload := &albWaf.CreateCustomRuleGroupPayload{ - Name: model.Name.ValueStringPointer(), + Name: model.Name.ValueString(), Rules: payloadRules, } @@ -607,38 +607,28 @@ func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (* } } - var operator *albWaf.ConditionOperator var operatorModel = OperatorModel{} - if !tfutils.IsUndefined(condition.Operator) { - diags = condition.Operator.As(ctx, &operatorModel, basetypes.ObjectAsOptions{}) - if diags.HasError() { - return nil, fmt.Errorf("converting operator: %v", diags.Errors()) - } - - operator = &albWaf.ConditionOperator{ - Type: (*albWaf.ConditionOperatorType)(operatorModel.Type.ValueStringPointer()), - Value: operatorModel.Value.ValueStringPointer(), - } + diags = condition.Operator.As(ctx, &operatorModel, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting operator: %v", diags.Errors()) } - var variable *albWaf.ConditionVariable var variableModel = VariableModel{} - if !tfutils.IsUndefined(condition.Variable) { - diags = condition.Variable.As(ctx, &variableModel, basetypes.ObjectAsOptions{}) - if diags.HasError() { - return nil, fmt.Errorf("converting variable: %v", diags.Errors()) - } - - variable = &albWaf.ConditionVariable{ - Type: (*albWaf.ConditionVariableType)(variableModel.Type.ValueStringPointer()), - Value: variableModel.Value.ValueStringPointer(), - } + diags = condition.Variable.As(ctx, &variableModel, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return nil, fmt.Errorf("converting variable: %v", diags.Errors()) } result = append(result, albWaf.Condition{ - Operator: operator, + Operator: albWaf.ConditionOperator{ + Type: albWaf.ConditionOperatorType(operatorModel.Type.ValueString()), + Value: operatorModel.Value.ValueStringPointer(), + }, Transformations: transformations, - Variable: variable, + Variable: albWaf.ConditionVariable{ + Type: albWaf.ConditionVariableType(variableModel.Type.ValueString()), + Value: variableModel.Value.ValueStringPointer(), + }, }) } } @@ -748,7 +738,7 @@ func mapConditions(ctx context.Context, rule albWaf.GetCustomRule) (*basetypes.L if operator, ok := condition.GetOperatorOk(); ok { operatorModel := OperatorModel{ - Type: types.StringPointerValue((*string)(operator.Type)), + Type: types.StringValue(string(operator.Type)), Value: types.StringPointerValue(operator.Value), } @@ -767,7 +757,7 @@ func mapConditions(ctx context.Context, rule albWaf.GetCustomRule) (*basetypes.L if variable, ok := condition.GetVariableOk(); ok { variableModel := VariableModel{ - Type: types.StringPointerValue((*string)(variable.Type)), + Type: types.StringValue(string(variable.Type)), Value: types.StringPointerValue(variable.Value), } diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 20474765b..5c9273d20 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -63,26 +63,26 @@ func TestToCreatePayload(t *testing.T) { }), }, expected: &albWaf.CreateCustomRuleGroupPayload{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: &albWaf.Behaviour{ - Action: new(albWaf.BehaviourAction("some-action")), + Behaviour: albWaf.Behaviour{ + Action: albWaf.BehaviourAction("some-action"), Log: new(true), LogMsg: new("Log: something happened"), }, Conditions: []albWaf.Condition{ { - Operator: &albWaf.ConditionOperator{ - Type: new(albWaf.ConditionOperatorType("operator-type")), + Operator: albWaf.ConditionOperator{ + Type: albWaf.ConditionOperatorType("operator-type"), Value: new("operator-value"), }, Transformations: []albWaf.ConditionTransformationsInner{ "foo", "bar", }, - Variable: &albWaf.ConditionVariable{ - Type: new(albWaf.ConditionVariableType("variable-type")), + Variable: albWaf.ConditionVariable{ + Type: albWaf.ConditionVariableType("variable-type"), Value: new("variable-value"), }, }, @@ -127,15 +127,15 @@ func TestToCreatePayload(t *testing.T) { }), }, expected: &albWaf.CreateCustomRuleGroupPayload{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: &albWaf.Behaviour{}, + Behaviour: albWaf.Behaviour{}, Conditions: []albWaf.Condition{ { - Operator: &albWaf.ConditionOperator{}, + Operator: albWaf.ConditionOperator{}, Transformations: []albWaf.ConditionTransformationsInner{}, - Variable: &albWaf.ConditionVariable{}, + Variable: albWaf.ConditionVariable{}, }, }, }, @@ -152,7 +152,7 @@ func TestToCreatePayload(t *testing.T) { Region: testRegion, }, expected: &albWaf.CreateCustomRuleGroupPayload{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{}, }, isValid: true, @@ -206,16 +206,16 @@ func TestMapFields(t *testing.T) { }, Conditions: []albWaf.Condition{ { - Operator: &albWaf.ConditionOperator{ - Type: new(albWaf.ConditionOperatorType("operator-type")), + Operator: albWaf.ConditionOperator{ + Type: albWaf.ConditionOperatorType("operator-type"), Value: new("operator-value"), }, Transformations: []albWaf.ConditionTransformationsInner{ "foo", "bar", }, - Variable: &albWaf.ConditionVariable{ - Type: new(albWaf.ConditionVariableType("variable-type")), + Variable: albWaf.ConditionVariable{ + Type: albWaf.ConditionVariableType("variable-type"), Value: new("variable-value"), }, }, diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource.go b/stackit/internal/services/albwaf/managed_rule_set/resource.go index a63718da0..37f4bb9d5 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource.go @@ -432,8 +432,8 @@ func toCreatePayload(_ context.Context, model *Model) (*albWaf.CreateManagedRule } payload := &albWaf.CreateManagedRuleSetPayload{ - Name: model.Name.ValueStringPointer(), - Type: new(albWaf.MRSType(model.Type.ValueString())), + Name: model.Name.ValueString(), + Type: albWaf.MRSType(model.Type.ValueString()), } return payload, nil diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go index 9b7bc9548..0158f48f6 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go @@ -36,8 +36,8 @@ func TestToCreatePayload(t *testing.T) { Type: types.StringValue(string(albWaf.MRSTYPE_TYPE_OWASP_CRS)), }, expected: &albWaf.CreateManagedRuleSetPayload{ - Name: testName.ValueStringPointer(), - Type: new(albWaf.MRSTYPE_TYPE_OWASP_CRS), + Name: testName.ValueString(), + Type: albWaf.MRSTYPE_TYPE_OWASP_CRS, }, isValid: true, }, From 20a602a45d620e18a5e00aeef2ac267c14be6fb6 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Tue, 28 Jul 2026 16:48:03 +0200 Subject: [PATCH 04/17] add examples --- .../data-source.tf | 4 +++ .../resource.tf | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 examples/data-sources/stackit_alb_waf_custom_rule_group/data-source.tf create mode 100644 examples/resources/stackit_alb_waf_custom_rule_group/resource.tf diff --git a/examples/data-sources/stackit_alb_waf_custom_rule_group/data-source.tf b/examples/data-sources/stackit_alb_waf_custom_rule_group/data-source.tf new file mode 100644 index 000000000..1182aaac4 --- /dev/null +++ b/examples/data-sources/stackit_alb_waf_custom_rule_group/data-source.tf @@ -0,0 +1,4 @@ +data "stackit_alb_waf_custom_rule_group" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-custom-rule-group" +} diff --git a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf new file mode 100644 index 000000000..3cc262086 --- /dev/null +++ b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf @@ -0,0 +1,29 @@ +resource "stackit_alb_waf_custom_rule_group" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-custom-rule-group" + rules = [ + { + description = "My custom rule group" + behaviour = { + action = "ACTION_DENY" + log = true + logMsg = "Some custom notification message string" + } + conditions = [ + { + operator = { + type = "OPERATOR_BEGINS_WITH" + value = "allowed objects" + } + transformations = [ + "TRANSFORMATION_LOWERCASE" + ] + variable = { + type = "VARIABLE_REQUEST_HEADERS" + value = "Host" + } + } + ] + } + ] +} From 0e097d774e172fe21eae80640b6c8bd8cf9bf135 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Tue, 28 Jul 2026 16:50:41 +0200 Subject: [PATCH 05/17] generate docs --- .../data-sources/alb_waf_custom_rule_group.md | 9 ++++- docs/resources/alb_waf_custom_rule_group.md | 34 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/docs/data-sources/alb_waf_custom_rule_group.md b/docs/data-sources/alb_waf_custom_rule_group.md index 0ed80c49e..8d9f70290 100644 --- a/docs/data-sources/alb_waf_custom_rule_group.md +++ b/docs/data-sources/alb_waf_custom_rule_group.md @@ -13,7 +13,14 @@ ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified i ~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. - +## Example Usage + +```terraform +data "stackit_alb_waf_custom_rule_group" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-custom-rule-group" +} +``` ## Schema diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index 41e778dc8..7ace5c6aa 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -13,7 +13,39 @@ ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified i ~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. - +## Example Usage + +```terraform +resource "stackit_alb_waf_custom_rule_group" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-custom-rule-group" + rules = [ + { + description = "My custom rule group" + behaviour = { + action = "ACTION_DENY" + log = true + logMsg = "Some custom notification message string" + } + conditions = [ + { + operator = { + type = "OPERATOR_BEGINS_WITH" + value = "allowed objects" + } + transformations = [ + "TRANSFORMATION_LOWERCASE" + ] + variable = { + type = "VARIABLE_REQUEST_HEADERS" + value = "Host" + } + } + ] + } + ] +} +``` ## Schema From 5775ea973d6d528879cb605b6b73f792d0a0b0fc Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 29 Jul 2026 12:19:25 +0200 Subject: [PATCH 06/17] start renaming behaviour to behavior --- .../data-sources/alb_waf_custom_rule_group.md | 6 +- docs/resources/alb_waf_custom_rule_group.md | 8 +- .../resource.tf | 2 +- .../services/albwaf/albwaf_acc_test.go | 46 ++++---- .../albwaf/custom_rule_group/datasource.go | 12 +- .../albwaf/custom_rule_group/resource.go | 108 +++++++++--------- .../albwaf/custom_rule_group/resource_test.go | 8 +- .../albwaf/testdata/custom-rule-group-max.tf | 2 +- .../albwaf/testdata/custom-rule-group-min.tf | 2 +- 9 files changed, 97 insertions(+), 97 deletions(-) diff --git a/docs/data-sources/alb_waf_custom_rule_group.md b/docs/data-sources/alb_waf_custom_rule_group.md index 8d9f70290..c9155af67 100644 --- a/docs/data-sources/alb_waf_custom_rule_group.md +++ b/docs/data-sources/alb_waf_custom_rule_group.md @@ -45,13 +45,13 @@ data "stackit_alb_waf_custom_rule_group" "example" { Read-Only: -- `behaviour` (Attributes) (see [below for nested schema](#nestedatt--rules--behaviour)) +- `behavior` (Attributes) (see [below for nested schema](#nestedatt--rules--behavior)) - `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) - `description` (String) A clear description explaining the threat vector or criteria addressed by this rule. - `id` (Number) Backend auto-allocated unique rule ID within the valid 1-99999 threshold. - -### Nested Schema for `rules.behaviour` + +### Nested Schema for `rules.behavior` Read-Only: diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index 7ace5c6aa..c88421b56 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -22,7 +22,7 @@ resource "stackit_alb_waf_custom_rule_group" "example" { rules = [ { description = "My custom rule group" - behaviour = { + behavior = { action = "ACTION_DENY" log = true logMsg = "Some custom notification message string" @@ -70,7 +70,7 @@ resource "stackit_alb_waf_custom_rule_group" "example" { Required: -- `behaviour` (Attributes) (see [below for nested schema](#nestedatt--rules--behaviour)) +- `behavior` (Attributes) (see [below for nested schema](#nestedatt--rules--behavior)) - `conditions` (Attributes List) (see [below for nested schema](#nestedatt--rules--conditions)) Optional: @@ -81,8 +81,8 @@ Read-Only: - `id` (Number) Backend auto-allocated unique rule ID within the valid 1-99999 threshold. - -### Nested Schema for `rules.behaviour` + +### Nested Schema for `rules.behavior` Required: diff --git a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf index 3cc262086..b34c43a61 100644 --- a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf +++ b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf @@ -4,7 +4,7 @@ resource "stackit_alb_waf_custom_rule_group" "example" { rules = [ { description = "My custom rule group" - behaviour = { + behavior = { action = "ACTION_DENY" log = true logMsg = "Some custom notification message string" diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 8544e5f0f..38733bce9 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -102,9 +102,9 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), @@ -143,11 +143,11 @@ func TestAccCustomRuleGroupMin(t *testing.T) { "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", ), - resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), - // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), + // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), resource.TestCheckResourceAttrPair( - "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", - "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", ), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), @@ -194,9 +194,9 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["action"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", "false"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["action"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["operator_type"])), @@ -230,10 +230,10 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMax["description"])), // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_type"])), @@ -276,12 +276,12 @@ func TestAccCustomRuleGroupMax(t *testing.T) { "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id", ), - resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), - resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), - // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), + // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), resource.TestCheckResourceAttrPair( - "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", - "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity", + "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", + "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", ), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), @@ -332,10 +332,10 @@ func TestAccCustomRuleGroupMax(t *testing.T) { // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["description"])), // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.action", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["action"])), - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log_msg"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behaviour.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["action"])), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log"])), + // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log_msg"])), + // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["operator_type"])), diff --git a/stackit/internal/services/albwaf/custom_rule_group/datasource.go b/stackit/internal/services/albwaf/custom_rule_group/datasource.go index b10c9ec94..128fa3d67 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/datasource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/datasource.go @@ -97,24 +97,24 @@ func (r *customRuleGroupDataSource) Schema(_ context.Context, _ datasource.Schem Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ - "behaviour": schema.SingleNestedAttribute{ - Description: descriptions["behaviour"], + "behavior": schema.SingleNestedAttribute{ + Description: descriptions["behavior"], Computed: true, Attributes: map[string]schema.Attribute{ "action": schema.StringAttribute{ - Description: descriptions["behaviour_action"], + Description: descriptions["behavior_action"], Computed: true, }, "log": schema.BoolAttribute{ - Description: descriptions["behaviour_log"], + Description: descriptions["behavior_log"], Computed: true, }, "log_msg": schema.StringAttribute{ - Description: descriptions["behaviour_log_msg"], + Description: descriptions["behavior_log_msg"], Computed: true, }, "severity": schema.StringAttribute{ - Description: descriptions["behaviour_severity"], + Description: descriptions["behavior_severity"], Computed: true, }, }, diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index d49b422cd..d7721638b 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -57,14 +57,14 @@ type Model struct { } type RuleModel struct { - Behaviour types.Object `tfsdk:"behaviour"` + Behavior types.Object `tfsdk:"behavior"` Conditions types.List `tfsdk:"conditions"` Description types.String `tfsdk:"description"` Id types.Int32 `tfsdk:"id"` } var ruleType = map[string]attr.Type{ - "behaviour": types.ObjectType{AttrTypes: behaviourType}, + "behavior": types.ObjectType{AttrTypes: behaviorType}, "conditions": types.ListType{ ElemType: types.ObjectType{AttrTypes: conditionType}, }, @@ -72,14 +72,14 @@ var ruleType = map[string]attr.Type{ "id": types.Int32Type, } -type BehaviourModel struct { +type BehaviorModel struct { Action types.String `tfsdk:"action"` Log types.Bool `tfsdk:"log"` LogMsg types.String `tfsdk:"log_msg"` Severity types.String `tfsdk:"severity"` } -var behaviourType = map[string]attr.Type{ +var behaviorType = map[string]attr.Type{ "action": types.StringType, "log": types.BoolType, "log_msg": types.StringType, @@ -163,29 +163,29 @@ func (r *customRuleGroupResource) Metadata(_ context.Context, req resource.Metad // descriptions for the attributes in the Schema. var descriptions = map[string]string{ - "id": "Terraform's internal resource identifier. Structured as \"`project_id`,`region`,`name`\".", - "project_id": "STACKIT project ID associated with the ALB WAF Custom Rule Group.", - "region": "STACKIT region name the resource is located in. If not defined, the provider region is used.", - "name": "Custom rule group configuration name.", - "rules": "Enriched rules containing auto-generated IDs and computed severity values.", - "rule_behaviour": "Behaviour of the rule.", - "rule_condition": "Conditions for this rule (order matters, first condition match triggers execution).", - "rule_description": "A clear description explaining the threat vector or criteria addressed by this rule.", - "rule_id": "Backend auto-allocated unique rule ID within the valid 1-99999 threshold.", - "behaviour_action": "The protective stance action. ACTION_DENY forces a 403 status response code.", - "behaviour_log": "Determines whether an entry should be generated in the security ledger upon a rule hit.", - "behaviour_log_msg": "Custom notification message string mapped to underlying logdata contexts. Required if log is true.", - "behaviour_severity": "Severity classification metric used by internal analytics graphs.", - "operator": "The comparison logic executed against the transformed variable.", - "operator_type": "The operational evaluation type definition macro.", - "operator_value": "The text or rule regex pattern arguments applied inside the operator execution loop.", - "transformations": "Ordered normalization steps applied before the operator runs.", - "variable": "The part of the HTTP transaction to inspect.", - "variable_type": "The targeted validation engine variable macro.", - "variable_value": "Optional key element context for map variables (e.g., matching a 'Host' header key).", - "usage": "Tracking metrics for CRG resource utilization.", - "usage_count": "Number of WAF configurations actively using this rule group.", - "usage_items": "List of individual WAF configuration names that bind this rule group.", + "id": "Terraform's internal resource identifier. Structured as \"`project_id`,`region`,`name`\".", + "project_id": "STACKIT project ID associated with the ALB WAF Custom Rule Group.", + "region": "STACKIT region name the resource is located in. If not defined, the provider region is used.", + "name": "Custom rule group configuration name.", + "rules": "Enriched rules containing auto-generated IDs and computed severity values.", + "rule_behavior": "Behavior of the rule.", + "rule_condition": "Conditions for this rule (order matters, first condition match triggers execution).", + "rule_description": "A clear description explaining the threat vector or criteria addressed by this rule.", + "rule_id": "Backend auto-allocated unique rule ID within the valid 1-99999 threshold.", + "behavior_action": "The protective stance action. ACTION_DENY forces a 403 status response code.", + "behavior_log": "Determines whether an entry should be generated in the security ledger upon a rule hit.", + "behavior_log_msg": "Custom notification message string mapped to underlying logdata contexts. Required if log is true.", + "behavior_severity": "Severity classification metric used by internal analytics graphs.", + "operator": "The comparison logic executed against the transformed variable.", + "operator_type": "The operational evaluation type definition macro.", + "operator_value": "The text or rule regex pattern arguments applied inside the operator execution loop.", + "transformations": "Ordered normalization steps applied before the operator runs.", + "variable": "The part of the HTTP transaction to inspect.", + "variable_type": "The targeted validation engine variable macro.", + "variable_value": "Optional key element context for map variables (e.g., matching a 'Host' header key).", + "usage": "Tracking metrics for CRG resource utilization.", + "usage_count": "Number of WAF configurations actively using this rule group.", + "usage_items": "List of individual WAF configuration names that bind this rule group.", } func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -242,27 +242,27 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq }, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ - "behaviour": schema.SingleNestedAttribute{ - Description: descriptions["behaviour"], + "behavior": schema.SingleNestedAttribute{ + Description: descriptions["behavior"], Required: true, Attributes: map[string]schema.Attribute{ "action": schema.StringAttribute{ - Description: descriptions["behaviour_action"], + Description: descriptions["behavior_action"], Required: true, Validators: []validator.String{ stringvalidator.OneOf(actionOptions...), }, }, "log": schema.BoolAttribute{ - Description: descriptions["behaviour_log"], + Description: descriptions["behavior_log"], Optional: true, }, "log_msg": schema.StringAttribute{ - Description: descriptions["behaviour_log_msg"], + Description: descriptions["behavior_log_msg"], Optional: true, }, "severity": schema.StringAttribute{ - Description: descriptions["behaviour_severity"], + Description: descriptions["behavior_severity"], Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -555,11 +555,11 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul } for _, rule := range rules { - behaviour := BehaviourModel{} - if !tfutils.IsUndefined(rule.Behaviour) { - diags := rule.Behaviour.As(ctx, &behaviour, basetypes.ObjectAsOptions{}) + behavior := BehaviorModel{} + if !tfutils.IsUndefined(rule.Behavior) { + diags := rule.Behavior.As(ctx, &behavior, basetypes.ObjectAsOptions{}) if diags.HasError() { - return nil, fmt.Errorf("converting to rule behaviour: %v", diags.Errors()) + return nil, fmt.Errorf("converting to rule behavior: %v", diags.Errors()) } } @@ -570,9 +570,9 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul payloadRules = append(payloadRules, albWaf.CreateCustomRule{ Behaviour: albWaf.Behaviour{ - Action: albWaf.BehaviourAction(behaviour.Action.ValueString()), - Log: behaviour.Log.ValueBoolPointer(), - LogMsg: behaviour.LogMsg.ValueStringPointer(), + Action: albWaf.BehaviourAction(behavior.Action.ValueString()), + Log: behavior.Log.ValueBoolPointer(), + LogMsg: behavior.LogMsg.ValueStringPointer(), }, Conditions: *conditions, Description: rule.Description.ValueStringPointer(), @@ -675,11 +675,11 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li Description: types.StringPointerValue(rule.Description), } - behaviour, err := mapBehaviour(ctx, rule.Behaviour) - if err != nil || behaviour == nil { - return nil, fmt.Errorf("map behaviour: %w", err) + behavior, err := mapBehavior(ctx, rule.Behaviour) + if err != nil || behavior == nil { + return nil, fmt.Errorf("map behavior: %w", err) } - ruleTF.Behaviour = *behaviour + ruleTF.Behavior = *behavior conditions, err := mapConditions(ctx, rule) if err != nil || conditions == nil { @@ -704,24 +704,24 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li return &result, nil } -func mapBehaviour(ctx context.Context, behaviour *albWaf.GetBehaviour) (*basetypes.ObjectValue, error) { +func mapBehavior(ctx context.Context, behavior *albWaf.GetBehaviour) (*basetypes.ObjectValue, error) { var diags diag.Diagnostics var result basetypes.ObjectValue - if behaviour != nil { - behaviourModel := BehaviourModel{ - Action: types.StringPointerValue((*string)(behaviour.Action)), - Log: types.BoolPointerValue(behaviour.Log), - LogMsg: types.StringPointerValue(behaviour.LogMsg), - Severity: types.StringPointerValue((*string)(behaviour.Severity)), + if behavior != nil { + behaviorModel := BehaviorModel{ + Action: types.StringPointerValue((*string)(behavior.Action)), + Log: types.BoolPointerValue(behavior.Log), + LogMsg: types.StringPointerValue(behavior.LogMsg), + Severity: types.StringPointerValue((*string)(behavior.Severity)), } - result, diags = types.ObjectValueFrom(ctx, behaviourType, behaviourModel) + result, diags = types.ObjectValueFrom(ctx, behaviorType, behaviorModel) if diags.HasError() { - return nil, fmt.Errorf("creating behaviour object: %w", core.DiagsToError(diags)) + return nil, fmt.Errorf("creating behavior object: %w", core.DiagsToError(diags)) } } else { - result = types.ObjectNull(behaviourType) + result = types.ObjectNull(behaviorType) } return &result, nil diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 5c9273d20..9b6de9a52 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -35,7 +35,7 @@ func TestToCreatePayload(t *testing.T) { Region: testRegion, Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ types.ObjectValueMust(ruleType, map[string]attr.Value{ - "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "behavior": types.ObjectValueMust(behaviorType, map[string]attr.Value{ "action": types.StringValue("some-action"), "log": types.BoolValue(true), "log_msg": types.StringValue("Log: something happened"), @@ -102,7 +102,7 @@ func TestToCreatePayload(t *testing.T) { Region: testRegion, Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ types.ObjectValueMust(ruleType, map[string]attr.Value{ - "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "behavior": types.ObjectValueMust(behaviorType, map[string]attr.Value{ "action": types.StringNull(), "log": types.BoolNull(), "log_msg": types.StringNull(), @@ -240,7 +240,7 @@ func TestMapFields(t *testing.T) { Id: testId, Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ types.ObjectValueMust(ruleType, map[string]attr.Value{ - "behaviour": types.ObjectValueMust(behaviourType, map[string]attr.Value{ + "behavior": types.ObjectValueMust(behaviorType, map[string]attr.Value{ "action": types.StringValue("some-action"), "log": types.BoolValue(true), "log_msg": types.StringValue("Log: something happened"), @@ -299,7 +299,7 @@ func TestMapFields(t *testing.T) { Region: testRegion, Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ types.ObjectValueMust(ruleType, map[string]attr.Value{ - "behaviour": types.ObjectNull(behaviourType), + "behavior": types.ObjectNull(behaviorType), "conditions": types.ListNull(types.ObjectType{AttrTypes: conditionType}), "description": types.StringNull(), "id": types.Int32Null(), diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf index 6f6093981..74495fb3d 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf @@ -17,7 +17,7 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { rules = [ { description = var.description - behaviour = { + behavior = { action = var.action log = var.log logMsg = var.log_msg diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf index 29e7a71db..cf1c92e6b 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf @@ -10,7 +10,7 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { name = var.name rules = [ { - behaviour = { + behavior = { action = var.action } conditions = [ From 1b93670b70e6218f0691b418f02ac22c1e99bfac Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Fri, 31 Jul 2026 10:29:18 +0200 Subject: [PATCH 07/17] ignored linter warnings for generated structs --- .../internal/services/albwaf/custom_rule_group/resource.go | 4 ++-- .../services/albwaf/custom_rule_group/resource_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index d7721638b..89ad9ab82 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -569,7 +569,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul } payloadRules = append(payloadRules, albWaf.CreateCustomRule{ - Behaviour: albWaf.Behaviour{ + Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec Action: albWaf.BehaviourAction(behavior.Action.ValueString()), Log: behavior.Log.ValueBoolPointer(), LogMsg: behavior.LogMsg.ValueStringPointer(), @@ -675,7 +675,7 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li Description: types.StringPointerValue(rule.Description), } - behavior, err := mapBehavior(ctx, rule.Behaviour) + behavior, err := mapBehavior(ctx, rule.Behaviour) // nolint:misspell // Generated from API spec if err != nil || behavior == nil { return nil, fmt.Errorf("map behavior: %w", err) } diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 9b6de9a52..7c8ad6d54 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -66,7 +66,7 @@ func TestToCreatePayload(t *testing.T) { Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: albWaf.Behaviour{ + Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec Action: albWaf.BehaviourAction("some-action"), Log: new(true), LogMsg: new("Log: something happened"), @@ -130,7 +130,7 @@ func TestToCreatePayload(t *testing.T) { Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: albWaf.Behaviour{}, + Behaviour: albWaf.Behaviour{}, // nolint:misspell // Generated from API spec Conditions: []albWaf.Condition{ { Operator: albWaf.ConditionOperator{}, @@ -198,7 +198,7 @@ func TestMapFields(t *testing.T) { Name: testName.ValueStringPointer(), Rules: []albWaf.GetCustomRule{ { - Behaviour: &albWaf.GetBehaviour{ + Behaviour: &albWaf.GetBehaviour{ // nolint:misspell // Generated from API spec Action: new(albWaf.GetBehaviourAction("some-action")), Log: new(true), LogMsg: new("Log: something happened"), From 83ca2b24bba21dae03790c329e0142ac5d55dd21 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Fri, 31 Jul 2026 10:33:14 +0200 Subject: [PATCH 08/17] remove usage --- .../data-sources/alb_waf_custom_rule_group.md | 12 ---- docs/data-sources/alb_waf_managed_rule_set.md | 11 ---- docs/resources/alb_waf_custom_rule_group.md | 12 ---- docs/resources/alb_waf_managed_rule_set.md | 11 ---- .../services/albwaf/albwaf_acc_test.go | 18 ------ .../albwaf/custom_rule_group/datasource.go | 15 ----- .../albwaf/custom_rule_group/resource.go | 60 ------------------- .../albwaf/custom_rule_group/resource_test.go | 16 ----- .../albwaf/managed_rule_set/datasource.go | 16 ----- .../albwaf/managed_rule_set/resource.go | 47 --------------- 10 files changed, 218 deletions(-) diff --git a/docs/data-sources/alb_waf_custom_rule_group.md b/docs/data-sources/alb_waf_custom_rule_group.md index c9155af67..4adfc985d 100644 --- a/docs/data-sources/alb_waf_custom_rule_group.md +++ b/docs/data-sources/alb_waf_custom_rule_group.md @@ -38,7 +38,6 @@ data "stackit_alb_waf_custom_rule_group" "example" { - `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". - `rules` (Attributes List) Enriched rules containing auto-generated IDs and computed severity values. (see [below for nested schema](#nestedatt--rules)) -- `usage` (Attributes) Tracking metrics for CRG resource utilization. (see [below for nested schema](#nestedatt--usage)) ### Nested Schema for `rules` @@ -86,14 +85,3 @@ Read-Only: - `type` (String) The targeted validation engine variable macro. - `value` (String) Optional key element context for map variables (e.g., matching a 'Host' header key). - - - - - -### Nested Schema for `usage` - -Read-Only: - -- `count` (Number) Number of WAF configurations actively using this rule group. -- `items` (List of String) List of individual WAF configuration names that bind this rule group. diff --git a/docs/data-sources/alb_waf_managed_rule_set.md b/docs/data-sources/alb_waf_managed_rule_set.md index 1f335dabf..c14a428bb 100644 --- a/docs/data-sources/alb_waf_managed_rule_set.md +++ b/docs/data-sources/alb_waf_managed_rule_set.md @@ -39,7 +39,6 @@ data "stackit_alb_waf_managed_rule_set" "example" { - `groups` (Attributes Map) Inventory of all available Managed Rule Set groups and their current configuration. (see [below for nested schema](#nestedatt--groups)) - `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". - `type` (String) Type of the Managed Rule Set. -- `usage` (Attributes) Managed Rule Set usage (see [below for nested schema](#nestedatt--usage)) - `version` (String) Managed Rule Set version. @@ -59,13 +58,3 @@ Read-Only: - `description` (String) A description of what this rule does. - `mode` (String) The current mode of the rule. - `severity` (String) Impact level. - - - - -### Nested Schema for `usage` - -Read-Only: - -- `count` (Number) Number of WAFs using this Managed Rule Set. -- `items` (List of String) List of WAFs that use this Managed Rule Set. diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index c88421b56..e5bcee6a1 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -63,7 +63,6 @@ resource "stackit_alb_waf_custom_rule_group" "example" { ### Read-Only - `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". -- `usage` (Attributes) Tracking metrics for CRG resource utilization. (see [below for nested schema](#nestedatt--usage)) ### Nested Schema for `rules` @@ -132,14 +131,3 @@ Required: Optional: - `value` (String) Optional key element context for map variables (e.g., matching a 'Host' header key). - - - - - -### Nested Schema for `usage` - -Read-Only: - -- `count` (Number) Number of WAF configurations actively using this rule group. -- `items` (List of String) List of individual WAF configuration names that bind this rule group. diff --git a/docs/resources/alb_waf_managed_rule_set.md b/docs/resources/alb_waf_managed_rule_set.md index eeb2c93f3..390e30ded 100644 --- a/docs/resources/alb_waf_managed_rule_set.md +++ b/docs/resources/alb_waf_managed_rule_set.md @@ -40,7 +40,6 @@ resource "stackit_alb_waf_managed_rule_set" "example" { - `groups` (Attributes Map) Inventory of all available Managed Rule Set groups and their current configuration. (see [below for nested schema](#nestedatt--groups)) - `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`name`". -- `usage` (Attributes) Managed Rule Set usage (see [below for nested schema](#nestedatt--usage)) - `version` (String) Managed Rule Set version. @@ -60,13 +59,3 @@ Read-Only: - `description` (String) A description of what this rule does. - `mode` (String) The current mode of the rule. - `severity` (String) Impact level. - - - - -### Nested Schema for `usage` - -Read-Only: - -- `count` (Number) Number of WAFs using this Managed Rule Set. -- `items` (List of String) List of WAFs that use this Managed Rule Set. diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 38733bce9..7b5455f8a 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -110,8 +110,6 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["variable_type"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Data source @@ -154,8 +152,6 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["variable_type"])), - - resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Import @@ -202,8 +198,6 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["operator_type"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.#", "0"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["variable_type"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Deletion is done by the framework implicitly @@ -242,8 +236,6 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMax["transformation"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_type"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_value"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Data source @@ -291,8 +283,6 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMax["transformation"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_type"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMax["variable_value"])), - - resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Import @@ -344,8 +334,6 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.transformations.0", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["transformation"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.type", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["variable_type"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.variable.value", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["variable_value"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "usage.count", "0"), ), }, // Deletion is done by the framework implicitly @@ -368,8 +356,6 @@ func TestAccManagedRuleSet(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_alb_waf_managed_rule_set.managed_rule_set", "id"), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSet["name"])), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSet["type"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), }, // Data source @@ -395,8 +381,6 @@ func TestAccManagedRuleSet(t *testing.T) { ), resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSet["name"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSet["type"])), - - resource.TestCheckResourceAttr("data.stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), }, // Import @@ -432,8 +416,6 @@ func TestAccManagedRuleSet(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_alb_waf_managed_rule_set.managed_rule_set", "id"), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "name", testutil.ConvertConfigVariable(testManagedRuleSetUpdated()["name"])), resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "type", testutil.ConvertConfigVariable(testManagedRuleSetUpdated()["type"])), - - resource.TestCheckResourceAttr("stackit_alb_waf_managed_rule_set.managed_rule_set", "usage.count", "0"), ), }, // Deletion is done by the framework implicitly diff --git a/stackit/internal/services/albwaf/custom_rule_group/datasource.go b/stackit/internal/services/albwaf/custom_rule_group/datasource.go index 128fa3d67..9ff842a37 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/datasource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/datasource.go @@ -171,21 +171,6 @@ func (r *customRuleGroupDataSource) Schema(_ context.Context, _ datasource.Schem }, }, }, - "usage": schema.SingleNestedAttribute{ - Description: descriptions["usage"], - Computed: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int32Attribute{ - Description: descriptions["usage_count"], - Computed: true, - }, - "items": schema.ListAttribute{ - Description: descriptions["usage_items"], - Computed: true, - ElementType: types.StringType, - }, - }, - }, }, } } diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 89ad9ab82..6c3f18beb 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -53,7 +53,6 @@ type Model struct { Region types.String `tfsdk:"region"` Name types.String `tfsdk:"name"` Rules types.List `tfsdk:"rules"` - Usage types.Object `tfsdk:"usage"` } type RuleModel struct { @@ -118,16 +117,6 @@ var variableType = map[string]attr.Type{ "value": types.StringType, } -type UsageModel struct { - Count types.Int32 `tfsdk:"count"` - Items types.List `tfsdk:"items"` -} - -var usageType = map[string]attr.Type{ - "count": types.Int32Type, - "items": types.ListType{ElemType: types.StringType}, -} - type customRuleGroupResource struct { client *albWaf.APIClient providerData core.ProviderData @@ -183,9 +172,6 @@ var descriptions = map[string]string{ "variable": "The part of the HTTP transaction to inspect.", "variable_type": "The targeted validation engine variable macro.", "variable_value": "Optional key element context for map variables (e.g., matching a 'Host' header key).", - "usage": "Tracking metrics for CRG resource utilization.", - "usage_count": "Number of WAF configurations actively using this rule group.", - "usage_items": "List of individual WAF configuration names that bind this rule group.", } func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -336,21 +322,6 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq }, }, }, - "usage": schema.SingleNestedAttribute{ - Description: descriptions["usage"], - Computed: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int32Attribute{ - Description: descriptions["usage_count"], - Computed: true, - }, - "items": schema.ListAttribute{ - Description: descriptions["usage_items"], - Computed: true, - ElementType: types.StringType, - }, - }, - }, }, } } @@ -654,12 +625,6 @@ func mapFields(ctx context.Context, customRuleGroup *albWaf.GetCustomRuleGroupRe } model.Rules = *rules - usage, err := mapUsage(ctx, customRuleGroup.Usage) - if err != nil || usage == nil { - return fmt.Errorf("map usage: %w", err) - } - model.Usage = *usage - return nil } @@ -785,28 +750,3 @@ func mapConditions(ctx context.Context, rule albWaf.GetCustomRule) (*basetypes.L return &result, nil } - -func mapUsage(ctx context.Context, usage *albWaf.CRGUsage) (*basetypes.ObjectValue, error) { - var diags diag.Diagnostics - var result basetypes.ObjectValue - - if usage != nil { - usageModel := UsageModel{ - Count: types.Int32PointerValue(usage.Count), - } - - usageModel.Items, diags = types.ListValueFrom(ctx, types.StringType, usage.GetItems()) - if diags.HasError() { - return nil, fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) - } - - result, diags = types.ObjectValueFrom(ctx, usageType, usageModel) - if diags.HasError() { - return nil, fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) - } - } else { - result = types.ObjectNull(usageType) - } - - return &result, nil -} diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 7c8ad6d54..cb691f437 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -224,14 +224,6 @@ func TestMapFields(t *testing.T) { Id: new(int32(42)), }, }, - Usage: &albWaf.CRGUsage{ - Count: new(int32(42)), - Items: []string{ - "one", - "two", - "three", - }, - }, }, expected: &Model{ ProjectId: testProjectId, @@ -266,14 +258,6 @@ func TestMapFields(t *testing.T) { "id": types.Int32Value(42), }), }), - Usage: types.ObjectValueMust(usageType, map[string]attr.Value{ - "count": types.Int32Value(42), - "items": types.ListValueMust(types.StringType, []attr.Value{ - types.StringValue("one"), - types.StringValue("two"), - types.StringValue("three"), - }), - }), }, isValid: true, }, diff --git a/stackit/internal/services/albwaf/managed_rule_set/datasource.go b/stackit/internal/services/albwaf/managed_rule_set/datasource.go index b802907a4..f53205ae2 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/datasource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/datasource.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" @@ -100,21 +99,6 @@ func (d *managedRuleSetDataSource) Schema(_ context.Context, _ datasource.Schema Description: descriptions["version"], Computed: true, }, - "usage": schema.SingleNestedAttribute{ - Description: descriptions["usage"], - Computed: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int32Attribute{ - Description: descriptions["usage_count"], - Computed: true, - }, - "items": schema.ListAttribute{ - Description: descriptions["usage_items"], - Computed: true, - ElementType: types.StringType, - }, - }, - }, "groups": schema.MapNestedAttribute{ Description: descriptions["groups"], Computed: true, diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource.go b/stackit/internal/services/albwaf/managed_rule_set/resource.go index 37f4bb9d5..329e4192c 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource.go @@ -43,7 +43,6 @@ type Model struct { Name types.String `tfsdk:"name"` Groups types.Map `tfsdk:"groups"` Type types.String `tfsdk:"type"` - Usage types.Object `tfsdk:"usage"` Version types.String `tfsdk:"version"` } @@ -73,16 +72,6 @@ var ruleType = map[string]attr.Type{ "severity": types.StringType, } -type UsageModel struct { - Count types.Int32 `tfsdk:"count"` - Items types.List `tfsdk:"items"` -} - -var usageType = map[string]attr.Type{ - "count": types.Int32Type, - "items": types.ListType{ElemType: types.StringType}, -} - type managedRuleSetResource struct { client *albWaf.APIClient providerData core.ProviderData @@ -124,9 +113,6 @@ var descriptions = map[string]string{ "name": "Managed Rule Set configuration name.", "type": "Type of the Managed Rule Set.", "version": "Managed Rule Set version.", - "usage": "Managed Rule Set usage", - "usage_count": "Number of WAFs using this Managed Rule Set.", - "usage_items": "List of WAFs that use this Managed Rule Set.", "groups": "Inventory of all available Managed Rule Set groups and their current configuration.", "group_description": "A description of what this group covers.", "group_name": "The name for the rule group.", @@ -190,21 +176,6 @@ func (r *managedRuleSetResource) Schema(_ context.Context, _ resource.SchemaRequ Description: descriptions["version"], Computed: true, }, - "usage": schema.SingleNestedAttribute{ - Description: descriptions["usage"], - Computed: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int32Attribute{ - Description: descriptions["usage_count"], - Computed: true, - }, - "items": schema.ListAttribute{ - Description: descriptions["usage_items"], - Computed: true, - ElementType: types.StringType, - }, - }, - }, "groups": schema.MapNestedAttribute{ Description: descriptions["groups"], Computed: true, @@ -498,23 +469,5 @@ func mapFields(ctx context.Context, managedRuleSet *albWaf.GetManagedRuleSetResp return fmt.Errorf("mapping groups: %w", core.DiagsToError(diags)) } - if usage, ok := managedRuleSet.GetUsageOk(); ok { - usageModel := UsageModel{ - Count: types.Int32PointerValue(usage.Count), - } - - usageModel.Items, diags = types.ListValueFrom(ctx, types.StringType, usage.GetItems()) - if diags.HasError() { - return fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) - } - - model.Usage, diags = types.ObjectValueFrom(ctx, usageType, usageModel) - if diags.HasError() { - return fmt.Errorf("creating usage object: %w", core.DiagsToError(diags)) - } - } else { - model.Usage = types.ObjectNull(usageType) - } - return nil } From 3ce334b0150de8ed7f3b10b713ffbd84256e1374 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 3 Aug 2026 11:21:30 +0200 Subject: [PATCH 09/17] fix comments --- .../albwaf/custom_rule_group/resource.go | 30 ++++++++++++------- .../albwaf/custom_rule_group/resource_test.go | 5 +++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 6c3f18beb..09fb1230e 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -522,7 +522,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul rules := []RuleModel{} diags := model.Rules.ElementsAs(ctx, &rules, true) if diags.HasError() { - return nil, fmt.Errorf("converting to rule map: %v", diags.Errors()) + return nil, fmt.Errorf("converting to rule map: %w", core.DiagsToError(diags)) } for _, rule := range rules { @@ -530,13 +530,15 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul if !tfutils.IsUndefined(rule.Behavior) { diags := rule.Behavior.As(ctx, &behavior, basetypes.ObjectAsOptions{}) if diags.HasError() { - return nil, fmt.Errorf("converting to rule behavior: %v", diags.Errors()) + return nil, fmt.Errorf("converting to rule behavior: %w", core.DiagsToError(diags)) } } conditions, err := toConditionsPayload(ctx, rule.Conditions) - if err != nil || conditions == nil { + if err != nil { return nil, fmt.Errorf("converting conditions: %w", err) + } else if conditions == nil { + return nil, fmt.Errorf("conditions can not be empty") } payloadRules = append(payloadRules, albWaf.CreateCustomRule{ @@ -566,7 +568,7 @@ func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (* conditionModels := []ConditionModel{} diags := conditions.ElementsAs(ctx, &conditionModels, true) if diags.HasError() { - return nil, fmt.Errorf("converting to rule map: %v", diags.Errors()) + return nil, fmt.Errorf("converting to rule map: %w", core.DiagsToError(diags)) } for _, condition := range conditionModels { @@ -574,20 +576,20 @@ func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (* if !tfutils.IsUndefined(condition.Transformations) { diags := condition.Transformations.ElementsAs(ctx, &transformations, true) if diags.HasError() { - return nil, fmt.Errorf("converting transformations: %v", diags.Errors()) + return nil, fmt.Errorf("converting transformations: %w", core.DiagsToError(diags)) } } var operatorModel = OperatorModel{} diags = condition.Operator.As(ctx, &operatorModel, basetypes.ObjectAsOptions{}) if diags.HasError() { - return nil, fmt.Errorf("converting operator: %v", diags.Errors()) + return nil, fmt.Errorf("converting operator: %w", core.DiagsToError(diags)) } var variableModel = VariableModel{} diags = condition.Variable.As(ctx, &variableModel, basetypes.ObjectAsOptions{}) if diags.HasError() { - return nil, fmt.Errorf("converting variable: %v", diags.Errors()) + return nil, fmt.Errorf("converting variable: %w", core.DiagsToError(diags)) } result = append(result, albWaf.Condition{ @@ -616,12 +618,14 @@ func mapFields(ctx context.Context, customRuleGroup *albWaf.GetCustomRuleGroupRe } model.Id = tfutils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, model.Name.ValueString()) - model.Name = types.StringValue(model.Name.ValueString()) + model.Name = types.StringValue(customRuleGroup.GetName()) model.Region = types.StringValue(region) rules, err := mapRules(ctx, &customRuleGroup.Rules) - if err != nil || rules == nil { + if err != nil { return fmt.Errorf("map rules: %w", err) + } else if rules == nil { + return fmt.Errorf("rules can not be empty") } model.Rules = *rules @@ -641,14 +645,18 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li } behavior, err := mapBehavior(ctx, rule.Behaviour) // nolint:misspell // Generated from API spec - if err != nil || behavior == nil { + if err != nil { return nil, fmt.Errorf("map behavior: %w", err) + } else if behavior == nil { + return nil, fmt.Errorf("behavior can not be empty") } ruleTF.Behavior = *behavior conditions, err := mapConditions(ctx, rule) - if err != nil || conditions == nil { + if err != nil { return nil, fmt.Errorf("map conditions: %w", err) + } else if conditions == nil { + return nil, fmt.Errorf("conditions can not be empty") } ruleTF.Conditions = *conditions diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index cb691f437..0c941f082 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -272,6 +272,7 @@ func TestMapFields(t *testing.T) { }, region: testRegion.ValueString(), input: &albWaf.GetCustomRuleGroupResponse{ + Name: testName.ValueStringPointer(), Rules: []albWaf.GetCustomRule{ {}, }, @@ -301,7 +302,9 @@ func TestMapFields(t *testing.T) { Id: testId, }, region: testRegion.ValueString(), - input: &albWaf.GetCustomRuleGroupResponse{}, + input: &albWaf.GetCustomRuleGroupResponse{ + Name: testName.ValueStringPointer(), + }, expected: &Model{ Name: testName, Id: testId, From 0bde30f8cf1ba421db26e80c50c183e36791e0d0 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 3 Aug 2026 16:07:36 +0200 Subject: [PATCH 10/17] add update endpoint --- .../resource.tf | 2 +- go.mod | 2 +- go.sum | 4 +- .../services/albwaf/albwaf_acc_test.go | 5 +- .../albwaf/custom_rule_group/resource.go | 99 ++++++++++++++++--- .../albwaf/testdata/custom-rule-group-max.tf | 2 +- 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf index b34c43a61..c91a492dc 100644 --- a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf +++ b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf @@ -7,7 +7,7 @@ resource "stackit_alb_waf_custom_rule_group" "example" { behavior = { action = "ACTION_DENY" log = true - logMsg = "Some custom notification message string" + log_msg = "Some custom notification message string" } conditions = [ { diff --git a/go.mod b/go.mod index b870f2e66..614daed55 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/stackitcloud/stackit-sdk-go/core v0.26.0 github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 - github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0 + github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0 github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 github.com/stackitcloud/stackit-sdk-go/services/certificates v1.9.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 diff --git a/go.sum b/go.sum index 3accd98a5..84f6c37ad 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10 github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 h1:WoWlHdzISGXPEaJOYt6HP5F9M5nbyCJL6VqRJZIaOQs= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0/go.mod h1:eK6oRB5Tmpt6KbXQ4UYBGg2LgW5bPtVoncL9E8JSRww= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0 h1:ejTZTnGKFUWs9Ch9U30Jd+tpDA/SnHuSF9DpfD6w+To= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.11.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0 h1:H1Cv5GBJvUU9bRrMVobc8no+cDDsqcpcDA+EjdzmhIw= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY= github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 h1:k+KJ4gp9awhJMY5y55vDqRSr6G/S9+8haTNILGbgH9s= diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 7b5455f8a..3dbf5ffa1 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -66,7 +66,8 @@ var testCustomRuleGroupMax = config.Variables{ var testCustomRuleGroupMaxUpdated = func() config.Variables { updatedConfig := config.Variables{} maps.Copy(updatedConfig, testCustomRuleGroupMax) - updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"]))) + // Name should not be updated, test if the update works in place + updatedConfig["log_msg"] = config.StringVariable("foo-bar:") // updatedConfig["log"] = config.BoolVariable(false) return updatedConfig } @@ -309,7 +310,7 @@ func TestAccCustomRuleGroupMax(t *testing.T) { Config: fmt.Sprintf("%s\n%s", testutil.NewConfigBuilder().EnableBetaResources(true).BuildProviderConfig(), customRuleGroupMaxConfig), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction("stackit_alb_waf_custom_rule_group.custom_rule_group", plancheck.ResourceActionReplace), + plancheck.ExpectResourceAction("stackit_alb_waf_custom_rule_group.custom_rule_group", plancheck.ResourceActionUpdate), }, }, Check: resource.ComposeAggregateTestCheckFunc( diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 09fb1230e..b868e8bd9 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -15,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -220,9 +219,6 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq "rules": schema.ListNestedAttribute{ Description: descriptions["rules"], Required: true, - PlanModifiers: []planmodifier.List{ - listplanmodifier.RequiresReplace(), - }, Validators: []validator.List{ listvalidator.SizeAtLeast(1), }, @@ -431,8 +427,52 @@ func (r *customRuleGroupResource) Create(ctx context.Context, req resource.Creat tflog.Info(ctx, "ALB WAF Custom Rule Group created") } -func (r *customRuleGroupResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform - core.LogAndAddError(ctx, &resp.Diagnostics, "Ressource not updatable", "ALB WAF Custom Rule Group is not updatable") +func (r *customRuleGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform + var model Model + diags := req.Plan.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + ctx = core.InitProviderContext(ctx) + + projectId := model.ProjectId.ValueString() + customRuleGroupName := model.Name.ValueString() + region := model.Region.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "name", customRuleGroupName) + ctx = tflog.SetField(ctx, "region", region) + + payload, err := toUpdatePayload(ctx, &model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Creating API payload: %v", err)) + return + } + + updateResp, err := r.client.DefaultAPI.UpdateCustomRuleGroup(ctx, projectId, region, customRuleGroupName).UpdateCustomRuleGroupPayload(*payload).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Calling API to update export policy: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + + // map export policy + err = mapFields(ctx, updateResp, &model, region) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Processing API payload: %v", err)) + return + } + + // Set state to fully populated data + diags = resp.State.Set(ctx, model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Info(ctx, "ALB WAF Custom Rule Group update") } func (r *customRuleGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform @@ -517,10 +557,46 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul return nil, fmt.Errorf("nil model") } + payloadRules, err := toRulesPayload(ctx, model.Rules) + if err != nil { + return nil, fmt.Errorf("generating rules payload: %w", err) + } else if payloadRules == nil { + return nil, fmt.Errorf("rules can not be empty") + } + + payload := &albWaf.CreateCustomRuleGroupPayload{ + Name: model.Name.ValueString(), + Rules: *payloadRules, + } + + return payload, nil +} + +func toUpdatePayload(ctx context.Context, model *Model) (*albWaf.UpdateCustomRuleGroupPayload, error) { + if model == nil { + return nil, fmt.Errorf("nil model") + } + + payloadRules, err := toRulesPayload(ctx, model.Rules) + if err != nil { + return nil, fmt.Errorf("generating rules payload: %w", err) + } else if payloadRules == nil { + return nil, fmt.Errorf("rules can not be empty") + } + + payload := &albWaf.UpdateCustomRuleGroupPayload{ + Name: model.Name.ValueString(), + Rules: *payloadRules, + } + + return payload, nil +} + +func toRulesPayload(ctx context.Context, modelRules basetypes.ListValue) (*[]albWaf.CreateCustomRule, error) { payloadRules := []albWaf.CreateCustomRule{} - if !tfutils.IsUndefined(model.Rules) { + if !tfutils.IsUndefined(modelRules) { rules := []RuleModel{} - diags := model.Rules.ElementsAs(ctx, &rules, true) + diags := modelRules.ElementsAs(ctx, &rules, true) if diags.HasError() { return nil, fmt.Errorf("converting to rule map: %w", core.DiagsToError(diags)) } @@ -553,12 +629,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateCustomRul } } - payload := &albWaf.CreateCustomRuleGroupPayload{ - Name: model.Name.ValueString(), - Rules: payloadRules, - } - - return payload, nil + return &payloadRules, nil } func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (*[]albWaf.Condition, error) { diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf index 74495fb3d..738dee387 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf @@ -20,7 +20,7 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { behavior = { action = var.action log = var.log - logMsg = var.log_msg + log_msg = var.log_msg } conditions = [ { From 02b1e522aa20351fbaed6efea5ffe4c6a49a17d7 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 3 Aug 2026 16:34:21 +0200 Subject: [PATCH 11/17] generate docs --- docs/resources/alb_waf_custom_rule_group.md | 2 +- .../internal/services/albwaf/custom_rule_group/resource.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index e5bcee6a1..baf9ccf36 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -25,7 +25,7 @@ resource "stackit_alb_waf_custom_rule_group" "example" { behavior = { action = "ACTION_DENY" log = true - logMsg = "Some custom notification message string" + log_msg = "Some custom notification message string" } conditions = [ { diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index b868e8bd9..1d8657cd3 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -446,22 +446,21 @@ func (r *customRuleGroupResource) Update(ctx context.Context, req resource.Updat payload, err := toUpdatePayload(ctx, &model) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Creating API payload: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating ALB WAF Custom Rule Group", fmt.Sprintf("Creating API payload: %v", err)) return } updateResp, err := r.client.DefaultAPI.UpdateCustomRuleGroup(ctx, projectId, region, customRuleGroupName).UpdateCustomRuleGroupPayload(*payload).Execute() if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Calling API to update export policy: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating ALB WAF Custom Rule Group", fmt.Sprintf("Calling API update endpoint: %v", err)) return } ctx = core.LogResponse(ctx) - // map export policy err = mapFields(ctx, updateResp, &model, region) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating export policy", fmt.Sprintf("Processing API payload: %v", err)) + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating ALB WAF Custom Rule Group", fmt.Sprintf("Processing API payload: %v", err)) return } From 682b6cf874b1611e6185ca82ba97abb15c308b5a Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Tue, 4 Aug 2026 07:33:19 +0200 Subject: [PATCH 12/17] preview: v1beta --- .../resource.tf | 4 +-- go.mod | 2 +- go.sum | 4 +-- .../services/albwaf/albwaf_acc_test.go | 33 +++++++++++-------- .../albwaf/custom_rule_group/resource.go | 16 ++++----- .../albwaf/custom_rule_group/resource_test.go | 20 +++++------ .../albwaf/managed_rule_set/resource.go | 2 +- .../albwaf/managed_rule_set/resource_test.go | 10 +++--- .../albwaf/testdata/custom-rule-group-max.tf | 4 +-- 9 files changed, 51 insertions(+), 44 deletions(-) diff --git a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf index c91a492dc..efe0077d8 100644 --- a/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf +++ b/examples/resources/stackit_alb_waf_custom_rule_group/resource.tf @@ -5,8 +5,8 @@ resource "stackit_alb_waf_custom_rule_group" "example" { { description = "My custom rule group" behavior = { - action = "ACTION_DENY" - log = true + action = "ACTION_DENY" + log = true log_msg = "Some custom notification message string" } conditions = [ diff --git a/go.mod b/go.mod index 614daed55..06330a62a 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/stackitcloud/stackit-sdk-go/core v0.26.0 github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 - github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0 + github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7 github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 github.com/stackitcloud/stackit-sdk-go/services/certificates v1.9.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 diff --git a/go.sum b/go.sum index 84f6c37ad..2ab227668 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10 github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 h1:WoWlHdzISGXPEaJOYt6HP5F9M5nbyCJL6VqRJZIaOQs= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0/go.mod h1:eK6oRB5Tmpt6KbXQ4UYBGg2LgW5bPtVoncL9E8JSRww= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0 h1:H1Cv5GBJvUU9bRrMVobc8no+cDDsqcpcDA+EjdzmhIw= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7 h1:cN1tBet7jrZ8KAgWc8im57/Cp/sJ2JnoNiDCmz6p78Y= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY= github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 h1:k+KJ4gp9awhJMY5y55vDqRSr6G/S9+8haTNILGbgH9s= diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 3dbf5ffa1..95595ddc0 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -67,8 +67,15 @@ var testCustomRuleGroupMaxUpdated = func() config.Variables { updatedConfig := config.Variables{} maps.Copy(updatedConfig, testCustomRuleGroupMax) // Name should not be updated, test if the update works in place - updatedConfig["log_msg"] = config.StringVariable("foo-bar:") + updatedConfig["description"] = config.StringVariable("some description") + updatedConfig["action"] = config.StringVariable("ACTION_ALLOW") // updatedConfig["log"] = config.BoolVariable(false) + updatedConfig["log_msg"] = config.StringVariable("foo-bar:") + updatedConfig["operator_type"] = config.StringVariable("OPERATOR_CONTAINS") + updatedConfig["operator_value"] = config.StringVariable("bar") + updatedConfig["transformation"] = config.StringVariable("TRANSFORMATION_UTF8_TO_UNICODE") + updatedConfig["variable_type"] = config.StringVariable("VARIABLE_RESPONSE_HEADERS") + updatedConfig["variable_value"] = config.StringVariable("foo") return updatedConfig } @@ -101,11 +108,11 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMin["name"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMin["operator_type"])), @@ -189,11 +196,11 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["name"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["action"])), // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["operator_type"])), @@ -223,12 +230,12 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMax["description"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMax["operator_type"])), @@ -271,7 +278,7 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMax["action"])), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log"])), - // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMax["log_msg"])), resource.TestCheckResourceAttrPair( "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", @@ -320,13 +327,13 @@ func TestAccCustomRuleGroupMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "name", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["name"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.#", "1"), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["description"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rule.0.id"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.description", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["description"])), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["action"])), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log_msg"])), - // resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log_msg", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["log_msg"])), + resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.0.operator.type", testutil.ConvertConfigVariable(testCustomRuleGroupMaxUpdated()["operator_type"])), diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 1d8657cd3..5812a9700 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -40,10 +40,10 @@ var ( _ resource.ResourceWithImportState = &customRuleGroupResource{} _ resource.ResourceWithModifyPlan = &customRuleGroupResource{} - variableTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionVariableTypeEnumValues) - transformationOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionTransformationsInnerEnumValues) - operatorTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedConditionOperatorTypeEnumValues) - actionOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedBehaviourActionEnumValues) + variableTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedVariableEnumValues) + transformationOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedTransformationEnumValues) + operatorTypeOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedOperatorEnumValues) + actionOptions = sdkUtils.EnumSliceToStringSlice(albWaf.AllowedActionEnumValues) ) type Model struct { @@ -618,7 +618,7 @@ func toRulesPayload(ctx context.Context, modelRules basetypes.ListValue) (*[]alb payloadRules = append(payloadRules, albWaf.CreateCustomRule{ Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec - Action: albWaf.BehaviourAction(behavior.Action.ValueString()), + Action: albWaf.Action(behavior.Action.ValueString()), Log: behavior.Log.ValueBoolPointer(), LogMsg: behavior.LogMsg.ValueStringPointer(), }, @@ -642,7 +642,7 @@ func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (* } for _, condition := range conditionModels { - transformations := []albWaf.ConditionTransformationsInner{} + transformations := []albWaf.Transformation{} if !tfutils.IsUndefined(condition.Transformations) { diags := condition.Transformations.ElementsAs(ctx, &transformations, true) if diags.HasError() { @@ -664,12 +664,12 @@ func toConditionsPayload(ctx context.Context, conditions basetypes.ListValue) (* result = append(result, albWaf.Condition{ Operator: albWaf.ConditionOperator{ - Type: albWaf.ConditionOperatorType(operatorModel.Type.ValueString()), + Type: albWaf.Operator(operatorModel.Type.ValueString()), Value: operatorModel.Value.ValueStringPointer(), }, Transformations: transformations, Variable: albWaf.ConditionVariable{ - Type: albWaf.ConditionVariableType(variableModel.Type.ValueString()), + Type: albWaf.Variable(variableModel.Type.ValueString()), Value: variableModel.Value.ValueStringPointer(), }, }) diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index 0c941f082..cf666267c 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -67,22 +67,22 @@ func TestToCreatePayload(t *testing.T) { Rules: []albWaf.CreateCustomRule{ { Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec - Action: albWaf.BehaviourAction("some-action"), + Action: albWaf.Action("some-action"), Log: new(true), LogMsg: new("Log: something happened"), }, Conditions: []albWaf.Condition{ { Operator: albWaf.ConditionOperator{ - Type: albWaf.ConditionOperatorType("operator-type"), + Type: albWaf.Operator("operator-type"), Value: new("operator-value"), }, - Transformations: []albWaf.ConditionTransformationsInner{ + Transformations: []albWaf.Transformation{ "foo", "bar", }, Variable: albWaf.ConditionVariable{ - Type: albWaf.ConditionVariableType("variable-type"), + Type: albWaf.Variable("variable-type"), Value: new("variable-value"), }, }, @@ -134,7 +134,7 @@ func TestToCreatePayload(t *testing.T) { Conditions: []albWaf.Condition{ { Operator: albWaf.ConditionOperator{}, - Transformations: []albWaf.ConditionTransformationsInner{}, + Transformations: []albWaf.Transformation{}, Variable: albWaf.ConditionVariable{}, }, }, @@ -199,23 +199,23 @@ func TestMapFields(t *testing.T) { Rules: []albWaf.GetCustomRule{ { Behaviour: &albWaf.GetBehaviour{ // nolint:misspell // Generated from API spec - Action: new(albWaf.GetBehaviourAction("some-action")), + Action: new(albWaf.Action("some-action")), Log: new(true), LogMsg: new("Log: something happened"), - Severity: new(albWaf.GetBehaviourSeverity("critical")), + Severity: new(albWaf.Severity("critical")), }, Conditions: []albWaf.Condition{ { Operator: albWaf.ConditionOperator{ - Type: albWaf.ConditionOperatorType("operator-type"), + Type: albWaf.Operator("operator-type"), Value: new("operator-value"), }, - Transformations: []albWaf.ConditionTransformationsInner{ + Transformations: []albWaf.Transformation{ "foo", "bar", }, Variable: albWaf.ConditionVariable{ - Type: albWaf.ConditionVariableType("variable-type"), + Type: albWaf.Variable("variable-type"), Value: new("variable-value"), }, }, diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource.go b/stackit/internal/services/albwaf/managed_rule_set/resource.go index 329e4192c..32d543c8d 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource.go @@ -404,7 +404,7 @@ func toCreatePayload(_ context.Context, model *Model) (*albWaf.CreateManagedRule payload := &albWaf.CreateManagedRuleSetPayload{ Name: model.Name.ValueString(), - Type: albWaf.MRSType(model.Type.ValueString()), + Type: albWaf.Type(model.Type.ValueString()), } return payload, nil diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go index 0158f48f6..1e4beea6c 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go @@ -33,11 +33,11 @@ func TestToCreatePayload(t *testing.T) { Id: testId, ProjectId: testProjectId, Region: testRegion, - Type: types.StringValue(string(albWaf.MRSTYPE_TYPE_OWASP_CRS)), + Type: types.StringValue(string(albWaf.TYPE_TYPE_OWASP_CRS)), }, expected: &albWaf.CreateManagedRuleSetPayload{ Name: testName.ValueString(), - Type: albWaf.MRSTYPE_TYPE_OWASP_CRS, + Type: albWaf.TYPE_TYPE_OWASP_CRS, }, isValid: true, }, @@ -74,7 +74,7 @@ func TestMapFields(t *testing.T) { ProjectId: testProjectId, Region: testRegion, Name: testName, - Type: types.StringValue(string(albWaf.MRSTYPE_TYPE_OWASP_CRS)), + Type: types.StringValue(string(albWaf.TYPE_TYPE_OWASP_CRS)), Id: testId, Groups: types.MapValueMust(types.ObjectType{AttrTypes: ruleGroupType}, map[string]attr.Value{}), }, @@ -82,13 +82,13 @@ func TestMapFields(t *testing.T) { input: &albWaf.GetManagedRuleSetResponse{ Groups: &map[string]albWaf.MRSRuleGroup{}, Name: testName.ValueStringPointer(), - Type: new(albWaf.MRSTYPE2_TYPE_OWASP_CRS), + Type: new(albWaf.TYPE_TYPE_OWASP_CRS), }, expected: &Model{ ProjectId: testProjectId, Region: testRegion, Name: testName, - Type: types.StringValue(string(albWaf.MRSTYPE_TYPE_OWASP_CRS)), + Type: types.StringValue(string(albWaf.TYPE_TYPE_OWASP_CRS)), Id: testId, Groups: types.MapValueMust(types.ObjectType{AttrTypes: ruleGroupType}, map[string]attr.Value{}), }, diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf index 738dee387..095df35cf 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-max.tf @@ -18,8 +18,8 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { { description = var.description behavior = { - action = var.action - log = var.log + action = var.action + log = var.log log_msg = var.log_msg } conditions = [ From 21b90a6443d593a641a73e3a8a8be8ffe540265c Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 5 Aug 2026 07:00:14 +0200 Subject: [PATCH 13/17] preview v1 --- docs/resources/alb_waf_custom_rule_group.md | 4 +- go.mod | 2 +- go.sum | 4 +- .../services/albwaf/albwaf_acc_test.go | 8 +- .../albwaf/custom_rule_group/datasource.go | 2 +- .../albwaf/custom_rule_group/resource.go | 128 +++++++++--------- .../albwaf/custom_rule_group/resource_test.go | 33 +++-- .../albwaf/managed_rule_set/datasource.go | 2 +- .../albwaf/managed_rule_set/resource.go | 24 ++-- .../albwaf/managed_rule_set/resource_test.go | 7 +- .../albwaf/testdata/custom-rule-group-min.tf | 3 +- .../internal/services/albwaf/utils/util.go | 2 +- .../services/albwaf/utils/util_test.go | 2 +- stackit/internal/validate/bool.go | 59 ++++++++ 14 files changed, 171 insertions(+), 109 deletions(-) create mode 100644 stackit/internal/validate/bool.go diff --git a/docs/resources/alb_waf_custom_rule_group.md b/docs/resources/alb_waf_custom_rule_group.md index baf9ccf36..cd8d5b9df 100644 --- a/docs/resources/alb_waf_custom_rule_group.md +++ b/docs/resources/alb_waf_custom_rule_group.md @@ -23,8 +23,8 @@ resource "stackit_alb_waf_custom_rule_group" "example" { { description = "My custom rule group" behavior = { - action = "ACTION_DENY" - log = true + action = "ACTION_DENY" + log = true log_msg = "Some custom notification message string" } conditions = [ diff --git a/go.mod b/go.mod index 06330a62a..671cea0cd 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/stackitcloud/stackit-sdk-go/core v0.26.0 github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 - github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7 + github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9 github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 github.com/stackitcloud/stackit-sdk-go/services/certificates v1.9.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 diff --git a/go.sum b/go.sum index 2ab227668..db7f99bf4 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10 github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 h1:WoWlHdzISGXPEaJOYt6HP5F9M5nbyCJL6VqRJZIaOQs= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0/go.mod h1:eK6oRB5Tmpt6KbXQ4UYBGg2LgW5bPtVoncL9E8JSRww= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7 h1:cN1tBet7jrZ8KAgWc8im57/Cp/sJ2JnoNiDCmz6p78Y= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260803161638-443734019ea7/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9 h1:zhCCZvOFjO+mjcmvfZe2S52gZoLZsIWAKg6zGd0x+gM= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY= github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 h1:k+KJ4gp9awhJMY5y55vDqRSr6G/S9+8haTNILGbgH9s= diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 95595ddc0..daf5072f9 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/stackitcloud/stackit-sdk-go/core/utils" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" @@ -67,14 +67,14 @@ var testCustomRuleGroupMaxUpdated = func() config.Variables { updatedConfig := config.Variables{} maps.Copy(updatedConfig, testCustomRuleGroupMax) // Name should not be updated, test if the update works in place - updatedConfig["description"] = config.StringVariable("some description") + updatedConfig["description"] = config.StringVariable("new description") updatedConfig["action"] = config.StringVariable("ACTION_ALLOW") // updatedConfig["log"] = config.BoolVariable(false) updatedConfig["log_msg"] = config.StringVariable("foo-bar:") - updatedConfig["operator_type"] = config.StringVariable("OPERATOR_CONTAINS") + updatedConfig["operator_type"] = config.StringVariable("OPERATOR_BEGINS_WITH") updatedConfig["operator_value"] = config.StringVariable("bar") updatedConfig["transformation"] = config.StringVariable("TRANSFORMATION_UTF8_TO_UNICODE") - updatedConfig["variable_type"] = config.StringVariable("VARIABLE_RESPONSE_HEADERS") + updatedConfig["variable_type"] = config.StringVariable("VARIABLE_ARGS_POST") updatedConfig["variable_value"] = config.StringVariable("foo") return updatedConfig } diff --git a/stackit/internal/services/albwaf/custom_rule_group/datasource.go b/stackit/internal/services/albwaf/custom_rule_group/datasource.go index 9ff842a37..51649be50 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/datasource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/datasource.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 5812a9700..9656efe2c 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -12,9 +12,12 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -22,7 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" @@ -238,10 +241,15 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq "log": schema.BoolAttribute{ Description: descriptions["behavior_log"], Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), }, "log_msg": schema.StringAttribute{ Description: descriptions["behavior_log_msg"], Optional: true, + Validators: []validator.String{ + validate.OnlyIfBool(path.MatchRelative().AtParent().AtName("log"), true), + }, }, "severity": schema.StringAttribute{ Description: descriptions["behavior_severity"], @@ -283,6 +291,8 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq stringvalidator.OneOf(transformationOptions...), ), }, + Computed: true, + Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), }, "variable": schema.SingleNestedAttribute{ Description: descriptions["variable"], @@ -398,16 +408,10 @@ func (r *customRuleGroupResource) Create(ctx context.Context, req resource.Creat ctx = core.LogResponse(ctx) - if createResp.Name == nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Custom Rule Group", "Got empty Custom Rule Group name") - return - } - customRuleGroupName := *createResp.Name - ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ "project_id": projectId, "region": region, - "name": customRuleGroupName, + "name": createResp.Name, }) if resp.Diagnostics.HasError() { return @@ -617,7 +621,7 @@ func toRulesPayload(ctx context.Context, modelRules basetypes.ListValue) (*[]alb } payloadRules = append(payloadRules, albWaf.CreateCustomRule{ - Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec + Behavior: albWaf.Behavior{ Action: albWaf.Action(behavior.Action.ValueString()), Log: behavior.Log.ValueBoolPointer(), LogMsg: behavior.LogMsg.ValueStringPointer(), @@ -710,11 +714,11 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li rulesList := []attr.Value{} for _, rule := range *rules { ruleTF := RuleModel{ - Id: types.Int32PointerValue(rule.Id), + Id: types.Int32Value(rule.Id), Description: types.StringPointerValue(rule.Description), } - behavior, err := mapBehavior(ctx, rule.Behaviour) // nolint:misspell // Generated from API spec + behavior, err := mapBehavior(ctx, rule.Behavior) if err != nil { return nil, fmt.Errorf("map behavior: %w", err) } else if behavior == nil { @@ -722,7 +726,7 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li } ruleTF.Behavior = *behavior - conditions, err := mapConditions(ctx, rule) + conditions, err := mapConditions(ctx, &rule) if err != nil { return nil, fmt.Errorf("map conditions: %w", err) } else if conditions == nil { @@ -747,80 +751,80 @@ func mapRules(ctx context.Context, rules *[]albWaf.GetCustomRule) (*basetypes.Li return &result, nil } -func mapBehavior(ctx context.Context, behavior *albWaf.GetBehaviour) (*basetypes.ObjectValue, error) { +func mapBehavior(ctx context.Context, behavior albWaf.GetBehavior) (*basetypes.ObjectValue, error) { var diags diag.Diagnostics var result basetypes.ObjectValue - if behavior != nil { - behaviorModel := BehaviorModel{ - Action: types.StringPointerValue((*string)(behavior.Action)), - Log: types.BoolPointerValue(behavior.Log), - LogMsg: types.StringPointerValue(behavior.LogMsg), - Severity: types.StringPointerValue((*string)(behavior.Severity)), - } + behaviorModel := BehaviorModel{ + Action: types.StringValue(string(behavior.Action)), + Log: types.BoolValue(behavior.Log), + LogMsg: types.StringPointerValue(behavior.LogMsg), + Severity: types.StringValue(string(behavior.Severity)), + } - result, diags = types.ObjectValueFrom(ctx, behaviorType, behaviorModel) - if diags.HasError() { - return nil, fmt.Errorf("creating behavior object: %w", core.DiagsToError(diags)) - } - } else { - result = types.ObjectNull(behaviorType) + result, diags = types.ObjectValueFrom(ctx, behaviorType, behaviorModel) + if diags.HasError() { + return nil, fmt.Errorf("creating behavior object: %w", core.DiagsToError(diags)) } return &result, nil } -func mapConditions(ctx context.Context, rule albWaf.GetCustomRule) (*basetypes.ListValue, error) { +func mapConditions(ctx context.Context, rule *albWaf.GetCustomRule) (*basetypes.ListValue, error) { var diags diag.Diagnostics var result basetypes.ListValue - if conditions, ok := rule.GetConditionsOk(); ok { - conditionsList := []attr.Value{} - for _, condition := range conditions { - conditionTF := ConditionModel{} - - if operator, ok := condition.GetOperatorOk(); ok { - operatorModel := OperatorModel{ - Type: types.StringValue(string(operator.Type)), - Value: types.StringPointerValue(operator.Value), + if rule != nil { + if conditions, ok := rule.GetConditionsOk(); ok { + conditionsList := []attr.Value{} + for _, condition := range conditions { + conditionTF := ConditionModel{} + + if operator, ok := condition.GetOperatorOk(); ok { + operatorModel := OperatorModel{ + Type: types.StringValue(string(operator.Type)), + Value: types.StringPointerValue(operator.Value), + } + + conditionTF.Operator, diags = types.ObjectValueFrom(ctx, operatorType, operatorModel) + if diags.HasError() { + return nil, fmt.Errorf("creating operator object: %w", core.DiagsToError(diags)) + } + } else { + conditionTF.Operator = types.ObjectNull(operatorType) } - conditionTF.Operator, diags = types.ObjectValueFrom(ctx, operatorType, operatorModel) + conditionTF.Transformations, diags = types.ListValueFrom(ctx, types.StringType, condition.Transformations) if diags.HasError() { - return nil, fmt.Errorf("creating operator object: %w", core.DiagsToError(diags)) + return nil, fmt.Errorf("mapping transformations: %w", core.DiagsToError(diags)) } - } else { - conditionTF.Operator = types.ObjectNull(operatorType) - } - conditionTF.Transformations, diags = types.ListValueFrom(ctx, types.StringType, condition.Transformations) - if diags.HasError() { - return nil, fmt.Errorf("mapping transformations: %w", core.DiagsToError(diags)) - } - - if variable, ok := condition.GetVariableOk(); ok { - variableModel := VariableModel{ - Type: types.StringValue(string(variable.Type)), - Value: types.StringPointerValue(variable.Value), + if variable, ok := condition.GetVariableOk(); ok { + variableModel := VariableModel{ + Type: types.StringValue(string(variable.Type)), + Value: types.StringPointerValue(variable.Value), + } + + conditionTF.Variable, diags = types.ObjectValueFrom(ctx, variableType, variableModel) + if diags.HasError() { + return nil, fmt.Errorf("creating variable object: %w", core.DiagsToError(diags)) + } + } else { + conditionTF.Variable = types.ObjectNull(variableType) } - conditionTF.Variable, diags = types.ObjectValueFrom(ctx, variableType, variableModel) + condition, diags := types.ObjectValueFrom(ctx, conditionType, conditionTF) if diags.HasError() { - return nil, fmt.Errorf("creating variable object: %w", core.DiagsToError(diags)) + return nil, fmt.Errorf("mapping condition: %w", core.DiagsToError(diags)) } - } else { - conditionTF.Variable = types.ObjectNull(variableType) + conditionsList = append(conditionsList, condition) } - - condition, diags := types.ObjectValueFrom(ctx, conditionType, conditionTF) + result, diags = types.ListValue(types.ObjectType{AttrTypes: conditionType}, conditionsList) if diags.HasError() { - return nil, fmt.Errorf("mapping condition: %w", core.DiagsToError(diags)) + return nil, fmt.Errorf("mapping conditions: %w", core.DiagsToError(diags)) } - conditionsList = append(conditionsList, condition) - } - result, diags = types.ListValue(types.ObjectType{AttrTypes: conditionType}, conditionsList) - if diags.HasError() { - return nil, fmt.Errorf("mapping conditions: %w", core.DiagsToError(diags)) + } else { + result = types.ListNull(types.ObjectType{AttrTypes: conditionType}) } } else { result = types.ListNull(types.ObjectType{AttrTypes: conditionType}) diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go index cf666267c..d1b353049 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource_test.go @@ -9,7 +9,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" ) var ( @@ -66,7 +66,7 @@ func TestToCreatePayload(t *testing.T) { Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: albWaf.Behaviour{ // nolint:misspell // Generated from API spec + Behavior: albWaf.Behavior{ Action: albWaf.Action("some-action"), Log: new(true), LogMsg: new("Log: something happened"), @@ -130,7 +130,7 @@ func TestToCreatePayload(t *testing.T) { Name: testName.ValueString(), Rules: []albWaf.CreateCustomRule{ { - Behaviour: albWaf.Behaviour{}, // nolint:misspell // Generated from API spec + Behavior: albWaf.Behavior{}, Conditions: []albWaf.Condition{ { Operator: albWaf.ConditionOperator{}, @@ -195,14 +195,14 @@ func TestMapFields(t *testing.T) { }, region: testRegion.ValueString(), input: &albWaf.GetCustomRuleGroupResponse{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), Rules: []albWaf.GetCustomRule{ { - Behaviour: &albWaf.GetBehaviour{ // nolint:misspell // Generated from API spec - Action: new(albWaf.Action("some-action")), - Log: new(true), + Behavior: albWaf.GetBehavior{ + Action: albWaf.Action("some-action"), + Log: true, LogMsg: new("Log: something happened"), - Severity: new(albWaf.Severity("critical")), + Severity: albWaf.Severity("critical"), }, Conditions: []albWaf.Condition{ { @@ -221,7 +221,7 @@ func TestMapFields(t *testing.T) { }, }, Description: new("foo-bar"), - Id: new(int32(42)), + Id: int32(42), }, }, }, @@ -272,7 +272,7 @@ func TestMapFields(t *testing.T) { }, region: testRegion.ValueString(), input: &albWaf.GetCustomRuleGroupResponse{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), Rules: []albWaf.GetCustomRule{ {}, }, @@ -284,10 +284,15 @@ func TestMapFields(t *testing.T) { Region: testRegion, Rules: types.ListValueMust(types.ObjectType{AttrTypes: ruleType}, []attr.Value{ types.ObjectValueMust(ruleType, map[string]attr.Value{ - "behavior": types.ObjectNull(behaviorType), - "conditions": types.ListNull(types.ObjectType{AttrTypes: conditionType}), + "behavior": types.ObjectValueMust(behaviorType, map[string]attr.Value{ + "action": types.StringValue(""), + "log": types.BoolValue(false), + "log_msg": types.StringNull(), + "severity": types.StringValue(""), + }), + "conditions": types.ListValueMust(types.ObjectType{AttrTypes: conditionType}, []attr.Value{}), "description": types.StringNull(), - "id": types.Int32Null(), + "id": types.Int32Value(0), }), }), }, @@ -303,7 +308,7 @@ func TestMapFields(t *testing.T) { }, region: testRegion.ValueString(), input: &albWaf.GetCustomRuleGroupResponse{ - Name: testName.ValueStringPointer(), + Name: testName.ValueString(), }, expected: &Model{ Name: testName, diff --git a/stackit/internal/services/albwaf/managed_rule_set/datasource.go b/stackit/internal/services/albwaf/managed_rule_set/datasource.go index f53205ae2..66956272b 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/datasource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/datasource.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource.go b/stackit/internal/services/albwaf/managed_rule_set/resource.go index 32d543c8d..8627854ba 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource.go @@ -19,7 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -292,16 +292,10 @@ func (r *managedRuleSetResource) Create(ctx context.Context, req resource.Create ctx = core.LogResponse(ctx) - if createResp.Name == nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating ALB WAF Managed Rule Set", "Got empty Managed Rule Set name") - return - } - managedRuleSetName := *createResp.Name - ctx = tfutils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ "project_id": projectId, "region": region, - "name": managedRuleSetName, + "name": createResp.Name, }) if resp.Diagnostics.HasError() { return @@ -424,24 +418,24 @@ func mapFields(ctx context.Context, managedRuleSet *albWaf.GetManagedRuleSetResp model.Name = types.StringValue(model.Name.ValueString()) model.Region = types.StringValue(region) - model.Type = types.StringPointerValue((*string)(managedRuleSet.Type)) - model.Version = types.StringPointerValue(managedRuleSet.Version) + model.Type = types.StringValue(string(managedRuleSet.Type)) + model.Version = types.StringValue(managedRuleSet.Version) groupsMap := map[string]attr.Value{} if groups, ok := managedRuleSet.GetGroupsOk(); ok { for groupKey, group := range *groups { groupTF := RuleGroupModel{ - Description: types.StringPointerValue(group.Description), - GroupName: types.StringPointerValue(group.GroupName), + Description: types.StringValue(group.Description), + GroupName: types.StringValue(group.GroupName), } ruleMap := map[string]attr.Value{} if rules, ok := group.GetRulesOk(); ok { for ruleKey, rule := range *rules { ruleTF := RuleModel{ - Description: types.StringPointerValue(rule.Description), - Mode: types.StringPointerValue((*string)(rule.Mode)), - Severity: types.StringPointerValue(rule.Severity), + Description: types.StringValue(rule.Description), + Mode: types.StringValue(string(rule.Mode)), + Severity: types.StringValue(rule.Severity), } ruleMap[ruleKey], diags = types.ObjectValueFrom(ctx, ruleType, ruleTF) diff --git a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go index 1e4beea6c..5271a5381 100644 --- a/stackit/internal/services/albwaf/managed_rule_set/resource_test.go +++ b/stackit/internal/services/albwaf/managed_rule_set/resource_test.go @@ -9,7 +9,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" ) var ( @@ -81,8 +81,8 @@ func TestMapFields(t *testing.T) { region: testRegion.ValueString(), input: &albWaf.GetManagedRuleSetResponse{ Groups: &map[string]albWaf.MRSRuleGroup{}, - Name: testName.ValueStringPointer(), - Type: new(albWaf.TYPE_TYPE_OWASP_CRS), + Name: testName.ValueString(), + Type: albWaf.TYPE_TYPE_OWASP_CRS, }, expected: &Model{ ProjectId: testProjectId, @@ -91,6 +91,7 @@ func TestMapFields(t *testing.T) { Type: types.StringValue(string(albWaf.TYPE_TYPE_OWASP_CRS)), Id: testId, Groups: types.MapValueMust(types.ObjectType{AttrTypes: ruleGroupType}, map[string]attr.Value{}), + Version: types.StringValue(""), }, isValid: true, }, diff --git a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf index cf1c92e6b..139e55b45 100644 --- a/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf +++ b/stackit/internal/services/albwaf/testdata/custom-rule-group-min.tf @@ -16,8 +16,7 @@ resource "stackit_alb_waf_custom_rule_group" "custom_rule_group" { conditions = [ { operator = { - type = var.operator_type - value = "dummy" + type = var.operator_type } variable = { type = var.variable_type diff --git a/stackit/internal/services/albwaf/utils/util.go b/stackit/internal/services/albwaf/utils/util.go index 9ed484a50..fc02f8d89 100644 --- a/stackit/internal/services/albwaf/utils/util.go +++ b/stackit/internal/services/albwaf/utils/util.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/stackitcloud/stackit-sdk-go/core/config" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" diff --git a/stackit/internal/services/albwaf/utils/util_test.go b/stackit/internal/services/albwaf/utils/util_test.go index ccd9f606c..171ab8ebe 100644 --- a/stackit/internal/services/albwaf/utils/util_test.go +++ b/stackit/internal/services/albwaf/utils/util_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients" "github.com/stackitcloud/stackit-sdk-go/core/config" - albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1betaapi" + albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" diff --git a/stackit/internal/validate/bool.go b/stackit/internal/validate/bool.go new file mode 100644 index 000000000..319df27f5 --- /dev/null +++ b/stackit/internal/validate/bool.go @@ -0,0 +1,59 @@ +package validate + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// OnlyIfBoolValidator checks if this string attribute is set when a target bool is true. +type OnlyIfBoolValidator struct { + Target path.Expression + Value bool +} + +// Ensure the validator implements the String validator interface +var _ validator.String = OnlyIfBoolValidator{} + +func (v OnlyIfBoolValidator) Description(_ context.Context) string { + return "The attribute can only be set if the boolean is set to the provided Value." +} + +func (v OnlyIfBoolValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v OnlyIfBoolValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { // nolint:gocritic // function signature required by Terraform + expression := req.PathExpression.Merge(v.Target) + + matchedPaths, diags := req.Config.PathMatches(ctx, expression) + resp.Diagnostics.Append(diags...) + + for _, target := range matchedPaths { + var targetBool types.Bool + diags := req.Config.GetAttribute(ctx, target, &targetBool) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() || targetBool.IsUnknown() { + return + } + + if targetBool.ValueBool() != v.Value && !req.ConfigValue.IsNull() { + resp.Diagnostics.AddAttributeError( + req.Path, + "Attribute can not be set", + fmt.Sprintf("This attribute can only be configured when %q is set to %t.", target.String(), v.Value), + ) + } + } +} + +func OnlyIfBool(target path.Expression, value bool) validator.String { + return OnlyIfBoolValidator{ + Target: target, + Value: value, + } +} From c87c6b7b1326f199c10d419d364237d43d32fdd2 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 5 Aug 2026 15:19:50 +0200 Subject: [PATCH 14/17] add tests and update to ga version --- go.mod | 2 +- go.sum | 4 +- .../services/albwaf/albwaf_acc_test.go | 7 +- .../albwaf/custom_rule_group/resource.go | 5 +- stackit/internal/validate/bool.go | 2 +- stackit/internal/validate/bool_test.go | 117 ++++++++++++++++++ 6 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 stackit/internal/validate/bool_test.go diff --git a/go.mod b/go.mod index 671cea0cd..687c9530a 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/stackitcloud/stackit-sdk-go/core v0.26.0 github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 - github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9 + github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.13.0 github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 github.com/stackitcloud/stackit-sdk-go/services/certificates v1.9.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 diff --git a/go.sum b/go.sum index db7f99bf4..0c9fe380b 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10 github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0 h1:WoWlHdzISGXPEaJOYt6HP5F9M5nbyCJL6VqRJZIaOQs= github.com/stackitcloud/stackit-sdk-go/services/alb v0.16.0/go.mod h1:eK6oRB5Tmpt6KbXQ4UYBGg2LgW5bPtVoncL9E8JSRww= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9 h1:zhCCZvOFjO+mjcmvfZe2S52gZoLZsIWAKg6zGd0x+gM= -github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.12.1-0.20260804160414-4300cbfba3b9/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.13.0 h1:uuQDV7Q3ndFJhlBbE2SX9ft+AtBJIck8rEcPr1lIqVE= +github.com/stackitcloud/stackit-sdk-go/services/albwaf v0.13.0/go.mod h1:4M9G1I64kZwlXO32ZoIpt0GAN4SpZ1SYerwCVVIBGoE= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY= github.com/stackitcloud/stackit-sdk-go/services/cdn v1.19.0 h1:k+KJ4gp9awhJMY5y55vDqRSr6G/S9+8haTNILGbgH9s= diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index daf5072f9..496fe0794 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -69,7 +69,6 @@ var testCustomRuleGroupMaxUpdated = func() config.Variables { // Name should not be updated, test if the update works in place updatedConfig["description"] = config.StringVariable("new description") updatedConfig["action"] = config.StringVariable("ACTION_ALLOW") - // updatedConfig["log"] = config.BoolVariable(false) updatedConfig["log_msg"] = config.StringVariable("foo-bar:") updatedConfig["operator_type"] = config.StringVariable("OPERATOR_BEGINS_WITH") updatedConfig["operator_value"] = config.StringVariable("bar") @@ -111,7 +110,7 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), @@ -150,7 +149,7 @@ func TestAccCustomRuleGroupMin(t *testing.T) { ), resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMin["action"])), - // resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), + resource.TestCheckResourceAttr("data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), resource.TestCheckResourceAttrPair( "data.stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", "stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity", @@ -199,7 +198,7 @@ func TestAccCustomRuleGroupMin(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.id"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.action", testutil.ConvertConfigVariable(testCustomRuleGroupMinUpdated()["action"])), - // resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), + resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.log", "false"), resource.TestCheckResourceAttrSet("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.behavior.severity"), resource.TestCheckResourceAttr("stackit_alb_waf_custom_rule_group.custom_rule_group", "rules.0.conditions.#", "1"), diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 9656efe2c..66f09e9e0 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -15,9 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -242,11 +240,11 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq Description: descriptions["behavior_log"], Optional: true, Computed: true, - Default: booldefault.StaticBool(false), }, "log_msg": schema.StringAttribute{ Description: descriptions["behavior_log_msg"], Optional: true, + Computed: true, Validators: []validator.String{ validate.OnlyIfBool(path.MatchRelative().AtParent().AtName("log"), true), }, @@ -292,7 +290,6 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq ), }, Computed: true, - Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), }, "variable": schema.SingleNestedAttribute{ Description: descriptions["variable"], diff --git a/stackit/internal/validate/bool.go b/stackit/internal/validate/bool.go index 319df27f5..6cd839645 100644 --- a/stackit/internal/validate/bool.go +++ b/stackit/internal/validate/bool.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// OnlyIfBoolValidator checks if this string attribute is set when a target bool is true. +// OnlyIfBoolValidator checks if this string attribute is set when a target bool equals the specified value. type OnlyIfBoolValidator struct { Target path.Expression Value bool diff --git a/stackit/internal/validate/bool_test.go b/stackit/internal/validate/bool_test.go new file mode 100644 index 000000000..940b1e0ed --- /dev/null +++ b/stackit/internal/validate/bool_test.go @@ -0,0 +1,117 @@ +package validate + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestOnlyIfBoolValidator(t *testing.T) { + tests := []struct { + description string + target types.Bool + expectedValue bool + isValid bool + }{ + { + description: "target true, expect true", + target: types.BoolValue(true), + expectedValue: true, + isValid: true, + }, + { + description: "target false, expect true", + target: types.BoolValue(false), + expectedValue: true, + isValid: false, + }, + { + description: "target false, expect false", + target: types.BoolValue(false), + expectedValue: false, + isValid: true, + }, + { + description: "target true, expect false", + target: types.BoolValue(true), + expectedValue: false, + isValid: false, + }, + { + description: "target unknown, expect true", + target: types.BoolUnknown(), + expectedValue: true, + isValid: true, + }, + { + description: "target unknown, expect false", + target: types.BoolUnknown(), + expectedValue: false, + isValid: true, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + ctx := context.Background() + + boolVal, err := tt.target.ToTerraformValue(ctx) + if err != nil { + t.Fatalf("Failed to convert bool to tftypes.Value: %s", err) + } + + objType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "target_bool": tftypes.Bool, + }, + } + rawConfig := tftypes.NewValue(objType, map[string]tftypes.Value{ + "target_bool": boolVal, + }) + + req := validator.StringRequest{ + Path: path.Root("my_string"), + PathExpression: path.MatchRoot("my_string"), + ConfigValue: types.StringValue("example_string"), + Config: tfsdk.Config{ + Raw: rawConfig, + Schema: schema.Schema{ + Attributes: map[string]schema.Attribute{ + "target_bool": schema.BoolAttribute{}, + }, + }, + }, + } + + resp := &validator.StringResponse{} + + OnlyIfBool(path.MatchRoot("target_bool"), tt.expectedValue).ValidateString(ctx, req, resp) + + if tt.isValid { + if resp.Diagnostics.HasError() { + t.Fatalf("did not expect validation error, got: %v", resp.Diagnostics) + } + } else { + hasExpectedError := false + + for _, diag := range resp.Diagnostics { + if diag.Summary() == "Attribute can not be set" { + hasExpectedError = true + } else { + t.Fatalf("expected validation error, got %q", diag.Summary()) + } + } + + if !hasExpectedError { + t.Fatalf("expected 'Attribute can not be set' error, got: %v", resp.Diagnostics) + } + } + }) + } +} From 7a4f38b95cb0c2e74621ba1421e43b90da615893 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 5 Aug 2026 15:56:56 +0200 Subject: [PATCH 15/17] moved validator to resource --- .../albwaf/custom_rule_group}/bool.go | 20 +++++++++---------- .../albwaf/custom_rule_group}/bool_test.go | 4 ++-- .../albwaf/custom_rule_group/resource.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) rename stackit/internal/{validate => services/albwaf/custom_rule_group}/bool.go (58%) rename stackit/internal/{validate => services/albwaf/custom_rule_group}/bool_test.go (95%) diff --git a/stackit/internal/validate/bool.go b/stackit/internal/services/albwaf/custom_rule_group/bool.go similarity index 58% rename from stackit/internal/validate/bool.go rename to stackit/internal/services/albwaf/custom_rule_group/bool.go index 6cd839645..c407177ed 100644 --- a/stackit/internal/validate/bool.go +++ b/stackit/internal/services/albwaf/custom_rule_group/bool.go @@ -1,4 +1,4 @@ -package validate +package custom_rule_group import ( "context" @@ -9,24 +9,24 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// OnlyIfBoolValidator checks if this string attribute is set when a target bool equals the specified value. -type OnlyIfBoolValidator struct { +// OnlyAllowedIfBoolEqualsValidator prevents that this string attribute is set if a target bool does not equal the specified value. +type OnlyAllowedIfBoolEqualsValidator struct { Target path.Expression Value bool } // Ensure the validator implements the String validator interface -var _ validator.String = OnlyIfBoolValidator{} +var _ validator.String = OnlyAllowedIfBoolEqualsValidator{} -func (v OnlyIfBoolValidator) Description(_ context.Context) string { - return "The attribute can only be set if the boolean is set to the provided Value." +func (v OnlyAllowedIfBoolEqualsValidator) Description(_ context.Context) string { + return "The attribute can only be set if the boolean is set to the provided value." } -func (v OnlyIfBoolValidator) MarkdownDescription(ctx context.Context) string { +func (v OnlyAllowedIfBoolEqualsValidator) MarkdownDescription(ctx context.Context) string { return v.Description(ctx) } -func (v OnlyIfBoolValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { // nolint:gocritic // function signature required by Terraform +func (v OnlyAllowedIfBoolEqualsValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { // nolint:gocritic // function signature required by Terraform expression := req.PathExpression.Merge(v.Target) matchedPaths, diags := req.Config.PathMatches(ctx, expression) @@ -51,8 +51,8 @@ func (v OnlyIfBoolValidator) ValidateString(ctx context.Context, req validator.S } } -func OnlyIfBool(target path.Expression, value bool) validator.String { - return OnlyIfBoolValidator{ +func OnlyAllowedIfBoolEquals(target path.Expression, value bool) validator.String { + return OnlyAllowedIfBoolEqualsValidator{ Target: target, Value: value, } diff --git a/stackit/internal/validate/bool_test.go b/stackit/internal/services/albwaf/custom_rule_group/bool_test.go similarity index 95% rename from stackit/internal/validate/bool_test.go rename to stackit/internal/services/albwaf/custom_rule_group/bool_test.go index 940b1e0ed..a9d2910c7 100644 --- a/stackit/internal/validate/bool_test.go +++ b/stackit/internal/services/albwaf/custom_rule_group/bool_test.go @@ -1,4 +1,4 @@ -package validate +package custom_rule_group import ( "context" @@ -91,7 +91,7 @@ func TestOnlyIfBoolValidator(t *testing.T) { resp := &validator.StringResponse{} - OnlyIfBool(path.MatchRoot("target_bool"), tt.expectedValue).ValidateString(ctx, req, resp) + OnlyAllowedIfBoolEquals(path.MatchRoot("target_bool"), tt.expectedValue).ValidateString(ctx, req, resp) if tt.isValid { if resp.Diagnostics.HasError() { diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 66f09e9e0..5e93fb79c 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -246,7 +246,7 @@ func (r *customRuleGroupResource) Schema(_ context.Context, _ resource.SchemaReq Optional: true, Computed: true, Validators: []validator.String{ - validate.OnlyIfBool(path.MatchRelative().AtParent().AtName("log"), true), + OnlyAllowedIfBoolEquals(path.MatchRelative().AtParent().AtName("log"), true), }, }, "severity": schema.StringAttribute{ From 2476ee34f3e01e04361224411c253b3fe3ed2ddc Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 5 Aug 2026 16:01:32 +0200 Subject: [PATCH 16/17] add todo for improving validator --- stackit/internal/services/albwaf/custom_rule_group/bool.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stackit/internal/services/albwaf/custom_rule_group/bool.go b/stackit/internal/services/albwaf/custom_rule_group/bool.go index c407177ed..1984c47bd 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/bool.go +++ b/stackit/internal/services/albwaf/custom_rule_group/bool.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +// TODO: will be moved to validators within STACKITTPR-786 + // OnlyAllowedIfBoolEqualsValidator prevents that this string attribute is set if a target bool does not equal the specified value. type OnlyAllowedIfBoolEqualsValidator struct { Target path.Expression From 1539e06b84def7c37109fdddf65abcff775e0c03 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Wed, 5 Aug 2026 16:25:48 +0200 Subject: [PATCH 17/17] fix acc tests --- .../services/albwaf/albwaf_acc_test.go | 12 +++++------ .../albwaf/custom_rule_group/resource.go | 21 ++++++++++++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/stackit/internal/services/albwaf/albwaf_acc_test.go b/stackit/internal/services/albwaf/albwaf_acc_test.go index 496fe0794..0c13bfe38 100644 --- a/stackit/internal/services/albwaf/albwaf_acc_test.go +++ b/stackit/internal/services/albwaf/albwaf_acc_test.go @@ -33,13 +33,11 @@ var ( ) var testCustomRuleGroupMin = config.Variables{ - "project_id": config.StringVariable(testutil.ProjectId), - "name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), - "action": config.StringVariable("ACTION_DENY"), - "operator_type": config.StringVariable("OPERATOR_VALIDATE_UTF8_ENCODING"), - "operator_value": config.StringVariable("foo"), - "transformation": config.StringVariable("TRANSFORMATION_LOWERCASE"), - "variable_type": config.StringVariable("VARIABLE_RESPONSE_STATUS"), + "project_id": config.StringVariable(testutil.ProjectId), + "name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "action": config.StringVariable("ACTION_DENY"), + "operator_type": config.StringVariable("OPERATOR_VALIDATE_UTF8_ENCODING"), + "variable_type": config.StringVariable("VARIABLE_RESPONSE_STATUS"), } var testCustomRuleGroupMinUpdated = func() config.Variables { diff --git a/stackit/internal/services/albwaf/custom_rule_group/resource.go b/stackit/internal/services/albwaf/custom_rule_group/resource.go index 5e93fb79c..540c0411d 100644 --- a/stackit/internal/services/albwaf/custom_rule_group/resource.go +++ b/stackit/internal/services/albwaf/custom_rule_group/resource.go @@ -617,14 +617,29 @@ func toRulesPayload(ctx context.Context, modelRules basetypes.ListValue) (*[]alb return nil, fmt.Errorf("conditions can not be empty") } + var log *bool + if !tfutils.IsUndefined(behavior.Log) { + log = behavior.Log.ValueBoolPointer() + } + + var logMsg *string + if !tfutils.IsUndefined(behavior.LogMsg) { + logMsg = behavior.LogMsg.ValueStringPointer() + } + + var description *string + if !tfutils.IsUndefined(rule.Description) { + description = rule.Description.ValueStringPointer() + } + payloadRules = append(payloadRules, albWaf.CreateCustomRule{ Behavior: albWaf.Behavior{ Action: albWaf.Action(behavior.Action.ValueString()), - Log: behavior.Log.ValueBoolPointer(), - LogMsg: behavior.LogMsg.ValueStringPointer(), + Log: log, + LogMsg: logMsg, }, Conditions: *conditions, - Description: rule.Description.ValueStringPointer(), + Description: description, }) } }