Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/resources/alb_waf_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,44 @@ ALB WAF Custom Rule Group resource schema. Uses the `default_region` specified i
## Example Usage

```terraform
resource "stackit_alb_waf_managed_rule_set" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-managed-rule-set"
type = "TYPE_OWASP_CRS"
}

resource "stackit_alb_waf_custom_rule_group" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-custom-rule-group"
rules = [
{
behavior = {
action = "ACTION_DENY"
}
conditions = [
{
operator = {
type = "OPERATOR_VALIDATE_UTF8_ENCODING"
}
variable = {
type = "VARIABLE_REQUEST_HEADERS"
}
}
]
}
]
}

resource "stackit_alb_waf_configuration" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-waf-configuration"

managed_rule_set_name = stackit_alb_waf_managed_rule_set.example.name
custom_rule_group_name = stackit_alb_waf_custom_rule_group.example.name

labels = {
"key" = "value"
}
}
```

Expand Down
36 changes: 35 additions & 1 deletion examples/resources/stackit_alb_waf_configuration/resource.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
resource "stackit_alb_waf_managed_rule_set" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-managed-rule-set"
type = "TYPE_OWASP_CRS"
}

resource "stackit_alb_waf_custom_rule_group" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-custom-rule-group"
rules = [
{
behavior = {
action = "ACTION_DENY"
}
conditions = [
{
operator = {
type = "OPERATOR_VALIDATE_UTF8_ENCODING"
}
variable = {
type = "VARIABLE_REQUEST_HEADERS"
}
}
]
}
]
}

resource "stackit_alb_waf_configuration" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "example-waf-configuration"
}

managed_rule_set_name = stackit_alb_waf_managed_rule_set.example.name
custom_rule_group_name = stackit_alb_waf_custom_rule_group.example.name

labels = {
"key" = "value"
}
}
34 changes: 34 additions & 0 deletions stackit/internal/services/albwaf/albwaf_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ func testAccCheckDestroy(s *terraform.State) error {
checkFunctions := []func(s *terraform.State) error{
testAlbWafCustomRuleGroupDestroy,
testAlbWafManagedRuleSetDestroy,
testAlbWafWafConfigurationDestroy,
}
var errs []error

Expand Down Expand Up @@ -743,3 +744,36 @@ func testAlbWafManagedRuleSetDestroy(s *terraform.State) error {
}
return nil
}

func testAlbWafWafConfigurationDestroy(s *terraform.State) error {
ctx := context.Background()
client, err := createClient()
if err != nil {
return err
}

wafConfigurationToDestroy := []string{}
for _, rs := range s.RootModule().Resources {
if rs.Type != "stackit_alb_waf_configuration" {
continue
}
// waf configuration transform id: "[projectId],[region],[name]"
name := strings.Split(rs.Primary.ID, core.Separator)[2]
Comment on lines +760 to +761

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes I get an index out of bound panic on lines like these when running acceptance tests because rs.Primary.ID == "".
Id add an if here to prevent dangling resources.

wafConfigurationToDestroy = append(wafConfigurationToDestroy, name)
}

resp, err := client.DefaultAPI.ListWAF(ctx, testutil.ProjectId, testutil.Region).Execute()
if err != nil {
return fmt.Errorf("getting resp: %w", err)
}

for _, item := range resp.Items {
if utils.Contains(wafConfigurationToDestroy, item.GetName()) {
Comment on lines +765 to +771

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.api.stackit.cloud/documentation/alb-waf/version/v1#tag/WAF/operation/WAFService_ListWAF

Looks like ListWAF uses pagination, docs don't mention a default pageSize. Is it enough to return all WAFs created in our acceptance tests?

_, err := client.DefaultAPI.DeleteWAF(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
}
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ func (r *customRuleGroupResource) ModifyPlan(ctx context.Context, req resource.M
return
}

if !req.State.Raw.IsNull() {
var stateModel Model
resp.Diagnostics.Append(req.State.Get(ctx, &stateModel)...)
if !resp.Diagnostics.HasError() {
utils.WarnIfNameChanges(stateModel.Name, planModel.Name, "Custom Rule Group", &resp.Diagnostics)
}
}

resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() {
return
Expand Down
17 changes: 2 additions & 15 deletions stackit/internal/services/albwaf/managed_rule_set/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,24 +237,11 @@ func (r *managedRuleSetResource) ModifyPlan(ctx context.Context, req resource.Mo
return
}

// Warn the user if the name is changing, as this triggers a replacement.
// Deletion of the old resource will fail if another resource (e.g. stackit_alb_waf_configuration)
// still references this managed rule set.
if !req.State.Raw.IsNull() {
var stateModel Model
resp.Diagnostics.Append(req.State.Get(ctx, &stateModel)...)
if !resp.Diagnostics.HasError() && !stateModel.Name.IsNull() && !stateModel.Name.IsUnknown() {
if !planModel.Name.Equal(stateModel.Name) {
resp.Diagnostics.AddWarning(
"Managed Rule Set name change requires resource replacement",
fmt.Sprintf(
"Changing the \"name\" attribute from %q to %q will destroy and recreate this resource. "+
"If another resource (e.g. \"stackit_alb_waf_configuration\") references this managed rule set "+
"by name, the replacement will fail. Remove or update that dependency before applying this change.",
stateModel.Name.ValueString(), planModel.Name.ValueString(),
),
)
}
if !resp.Diagnostics.HasError() {
utils.WarnIfNameChanges(stateModel.Name, planModel.Name, "Managed Rule Set", &resp.Diagnostics)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same conditional logic as one file above

}
}

Expand Down
18 changes: 18 additions & 0 deletions stackit/internal/services/albwaf/utils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
albWaf "github.com/stackitcloud/stackit-sdk-go/services/albwaf/v1api"

Expand All @@ -29,3 +30,20 @@ func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags

return apiClient
}

func WarnIfNameChanges(stateName, planName types.String, resourceLabel string, diags *diag.Diagnostics) {
if utils.IsUndefined(stateName) {
return
}
if planName.Equal(stateName) {
return
}
diags.AddWarning(
fmt.Sprintf("%s name change requires resource replacement", resourceLabel),
fmt.Sprintf(
"Changing the \"name\" attribute from %q to %q will destroy and recreate this resource. "+
"If another resource references this %s by name, the replacement will fail. Remove or update that dependency before applying this change.",
stateName.ValueString(), planName.ValueString(), resourceLabel,
),
)
}
12 changes: 10 additions & 2 deletions stackit/internal/services/albwaf/waf_configuration/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ func (r *wafResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanReq
return
}

if !req.State.Raw.IsNull() {
var stateModel Model
resp.Diagnostics.Append(req.State.Get(ctx, &stateModel)...)
if !resp.Diagnostics.HasError() {
albwafUtils.WarnIfNameChanges(stateModel.Name, planModel.Name, "WAF Configuration", &resp.Diagnostics)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() {
return
Expand Down Expand Up @@ -407,7 +415,7 @@ func toUpdatePayload(ctx context.Context, model *Model) (*albWaf.UpdateWAFPayloa
}

var labels *map[string]string
if !(model.Labels.IsNull() || model.Labels.IsUnknown()) {
if !tfutils.IsUndefined(model.Labels) {
diags := model.Labels.ElementsAs(ctx, &labels, false)
if diags.HasError() {
return nil, core.DiagsToError(diags)
Expand All @@ -426,7 +434,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*albWaf.CreateWAFPayloa
}

var labels *map[string]string
if !(model.Labels.IsNull() || model.Labels.IsUnknown()) {
if !tfutils.IsUndefined(model.Labels) {
diags := model.Labels.ElementsAs(ctx, &labels, false)
if diags.HasError() {
return nil, core.DiagsToError(diags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const (
testCustomRuleGroupName = "crg_name"
)

func Test_mapFields(t *testing.T) {
func TestMapFields(t *testing.T) {
fixtureModel := func(mods ...func(*Model)) *Model {
m := Model{
Id: types.StringValue(fmt.Sprintf("pid,%s,name", testRegion)),
Expand Down Expand Up @@ -105,7 +105,7 @@ func Test_mapFields(t *testing.T) {
}
}

func Test_toCreatePayload(t *testing.T) {
func TestToCreatePayload(t *testing.T) {
tests := []struct {
name string
model *Model
Expand Down
Loading