Skip to content

Commit a8538d7

Browse files
authored
Support spaces for detection rules (#1457)
1 parent 43086fe commit a8538d7

File tree

8 files changed

+951
-741
lines changed

8 files changed

+951
-741
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Add new `elasticstack_elasticsearch_ml_datafeed_state` resource ([#1422](https://github.com/elastic/terraform-provider-elasticstack/pull/1422))
1010
- Add `output_id` to `elasticstack_fleet_integration_policy` resource ([#1445](https://github.com/elastic/terraform-provider-elasticstack/pull/1445))
1111
- Make `hosts` attribute required in `elasticstack_fleet_output` resource ([#1450](https://github.com/elastic/terraform-provider-elasticstack/pull/1450/files))
12+
- Fix `elasticstack_kibana_security_detection_rule` to properly respect `space_id`
1213

1314
## [0.12.1] - 2025-10-22
1415
- Fix regression restricting the characters in an `elasticstack_elasticsearch_role_mapping` `name`. ([#1373](https://github.com/elastic/terraform-provider-elasticstack/pull/1373))

generated/kbapi/kibana.gen.go

Lines changed: 771 additions & 736 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated/kbapi/transform_schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,7 @@ func transformKibanaPaths(schema *Schema) {
690690
"/api/maintenance_window/{id}",
691691
"/api/actions/connector/{id}",
692692
"/api/actions/connectors",
693+
"/api/detection_engine/rules",
693694
}
694695

695696
// Add a spaceId parameter if not already present

internal/kibana/security_detection_rule/acc_test.go

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ func TestAccResourceSecurityDetectionRule_Query(t *testing.T) {
243243
resource.TestCheckNoResourceAttr(resourceName, "filters"),
244244
),
245245
},
246+
{
247+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minResponseActionVersionSupport),
248+
ResourceName: resourceName,
249+
ImportState: true,
250+
ImportStateVerify: true,
251+
},
246252
},
247253
})
248254
}
@@ -1367,7 +1373,7 @@ func testAccCheckSecurityDetectionRuleDestroy(s *terraform.State) error {
13671373
Id: &ruleObjectId,
13681374
}
13691375

1370-
response, err := kbClient.API.ReadRuleWithResponse(context.Background(), params)
1376+
response, err := kbClient.API.ReadRuleWithResponse(context.Background(), parts[0], params)
13711377
if err != nil {
13721378
return fmt.Errorf("failed to read security detection rule: %v", err)
13731379
}
@@ -3694,6 +3700,113 @@ resource "elasticstack_kibana_security_detection_rule" "test" {
36943700
`, name)
36953701
}
36963702

3703+
func TestAccResourceSecurityDetectionRule_QueryMinimalWithSpace(t *testing.T) {
3704+
resourceName := "elasticstack_kibana_security_detection_rule.test"
3705+
spaceResourceName := "elasticstack_kibana_space.test"
3706+
spaceID := fmt.Sprintf("test-space-%s", uuid.New().String()[:8])
3707+
3708+
resource.Test(t, resource.TestCase{
3709+
PreCheck: func() { acctest.PreCheck(t) },
3710+
ProtoV6ProviderFactories: acctest.Providers,
3711+
CheckDestroy: testAccCheckSecurityDetectionRuleDestroy,
3712+
Steps: []resource.TestStep{
3713+
{
3714+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionSupport),
3715+
Config: testAccSecurityDetectionRuleConfig_queryMinimalWithSpace("test-query-rule-with-space", spaceID),
3716+
Check: resource.ComposeTestCheckFunc(
3717+
// Check space attributes
3718+
resource.TestCheckResourceAttr(spaceResourceName, "space_id", spaceID),
3719+
resource.TestCheckResourceAttr(spaceResourceName, "name", "Test Space for Detection Rules"),
3720+
3721+
// Check detection rule attributes
3722+
resource.TestCheckResourceAttr(resourceName, "name", "test-query-rule-with-space"),
3723+
resource.TestCheckResourceAttr(resourceName, "type", "query"),
3724+
resource.TestCheckResourceAttr(resourceName, "query", "*:*"),
3725+
resource.TestCheckResourceAttr(resourceName, "language", "kuery"),
3726+
resource.TestCheckResourceAttr(resourceName, "enabled", "true"),
3727+
resource.TestCheckResourceAttr(resourceName, "description", "Minimal test query security detection rule in custom space"),
3728+
resource.TestCheckResourceAttr(resourceName, "severity", "low"),
3729+
resource.TestCheckResourceAttr(resourceName, "risk_score", "21"),
3730+
resource.TestCheckResourceAttr(resourceName, "index.0", "logs-*"),
3731+
resource.TestCheckResourceAttr(resourceName, "space_id", spaceID),
3732+
3733+
// Verify required fields are set
3734+
resource.TestCheckResourceAttrSet(resourceName, "id"),
3735+
resource.TestCheckResourceAttrSet(resourceName, "rule_id"),
3736+
resource.TestCheckResourceAttrSet(resourceName, "created_at"),
3737+
resource.TestCheckResourceAttrSet(resourceName, "created_by"),
3738+
3739+
// Verify optional fields are not set
3740+
resource.TestCheckNoResourceAttr(resourceName, "data_view_id"),
3741+
resource.TestCheckNoResourceAttr(resourceName, "namespace"),
3742+
resource.TestCheckNoResourceAttr(resourceName, "rule_name_override"),
3743+
resource.TestCheckNoResourceAttr(resourceName, "timestamp_override"),
3744+
resource.TestCheckNoResourceAttr(resourceName, "timestamp_override_fallback_disabled"),
3745+
resource.TestCheckNoResourceAttr(resourceName, "filters"),
3746+
resource.TestCheckNoResourceAttr(resourceName, "investigation_fields"),
3747+
resource.TestCheckNoResourceAttr(resourceName, "risk_score_mapping"),
3748+
resource.TestCheckNoResourceAttr(resourceName, "related_integrations"),
3749+
resource.TestCheckNoResourceAttr(resourceName, "required_fields"),
3750+
resource.TestCheckNoResourceAttr(resourceName, "severity_mapping"),
3751+
resource.TestCheckNoResourceAttr(resourceName, "response_actions"),
3752+
resource.TestCheckNoResourceAttr(resourceName, "alert_suppression"),
3753+
resource.TestCheckNoResourceAttr(resourceName, "building_block_type"),
3754+
),
3755+
},
3756+
{
3757+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionSupport),
3758+
Config: testAccSecurityDetectionRuleConfig_queryMinimalWithSpaceUpdate("test-query-rule-with-space-updated", spaceID),
3759+
Check: resource.ComposeTestCheckFunc(
3760+
// Check space attributes remain the same
3761+
resource.TestCheckResourceAttr(spaceResourceName, "space_id", spaceID),
3762+
resource.TestCheckResourceAttr(spaceResourceName, "name", "Test Space for Detection Rules"),
3763+
3764+
// Check updated detection rule attributes
3765+
resource.TestCheckResourceAttr(resourceName, "name", "test-query-rule-with-space-updated"),
3766+
resource.TestCheckResourceAttr(resourceName, "type", "query"),
3767+
resource.TestCheckResourceAttr(resourceName, "query", "event.category:authentication"),
3768+
resource.TestCheckResourceAttr(resourceName, "language", "kuery"),
3769+
resource.TestCheckResourceAttr(resourceName, "enabled", "false"),
3770+
resource.TestCheckResourceAttr(resourceName, "description", "Updated minimal test query security detection rule in custom space"),
3771+
resource.TestCheckResourceAttr(resourceName, "severity", "medium"),
3772+
resource.TestCheckResourceAttr(resourceName, "risk_score", "55"),
3773+
resource.TestCheckResourceAttr(resourceName, "index.0", "logs-*"),
3774+
resource.TestCheckResourceAttr(resourceName, "index.1", "winlogbeat-*"),
3775+
resource.TestCheckResourceAttr(resourceName, "space_id", spaceID),
3776+
3777+
// Verify required fields are still set
3778+
resource.TestCheckResourceAttrSet(resourceName, "id"),
3779+
resource.TestCheckResourceAttrSet(resourceName, "rule_id"),
3780+
resource.TestCheckResourceAttrSet(resourceName, "created_at"),
3781+
resource.TestCheckResourceAttrSet(resourceName, "created_by"),
3782+
3783+
// Verify optional fields are still not set
3784+
resource.TestCheckNoResourceAttr(resourceName, "data_view_id"),
3785+
resource.TestCheckNoResourceAttr(resourceName, "namespace"),
3786+
resource.TestCheckNoResourceAttr(resourceName, "rule_name_override"),
3787+
resource.TestCheckNoResourceAttr(resourceName, "timestamp_override"),
3788+
resource.TestCheckNoResourceAttr(resourceName, "timestamp_override_fallback_disabled"),
3789+
resource.TestCheckNoResourceAttr(resourceName, "filters"),
3790+
resource.TestCheckNoResourceAttr(resourceName, "investigation_fields"),
3791+
resource.TestCheckNoResourceAttr(resourceName, "risk_score_mapping"),
3792+
resource.TestCheckNoResourceAttr(resourceName, "related_integrations"),
3793+
resource.TestCheckNoResourceAttr(resourceName, "required_fields"),
3794+
resource.TestCheckNoResourceAttr(resourceName, "severity_mapping"),
3795+
resource.TestCheckNoResourceAttr(resourceName, "response_actions"),
3796+
resource.TestCheckNoResourceAttr(resourceName, "alert_suppression"),
3797+
resource.TestCheckNoResourceAttr(resourceName, "building_block_type"),
3798+
),
3799+
},
3800+
{
3801+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionSupport),
3802+
ResourceName: resourceName,
3803+
ImportState: true,
3804+
ImportStateVerify: true,
3805+
},
3806+
},
3807+
})
3808+
}
3809+
36973810
func TestAccResourceSecurityDetectionRule_EQLMinimal(t *testing.T) {
36983811
resourceName := "elasticstack_kibana_security_detection_rule.test"
36993812

@@ -4625,6 +4738,66 @@ func TestAccResourceSecurityDetectionRule_QueryWithMitreThreat(t *testing.T) {
46254738
})
46264739
}
46274740

4741+
func testAccSecurityDetectionRuleConfig_queryMinimalWithSpace(name, spaceID string) string {
4742+
return fmt.Sprintf(`
4743+
provider "elasticstack" {
4744+
kibana {}
4745+
}
4746+
4747+
resource "elasticstack_kibana_space" "test" {
4748+
space_id = "%s"
4749+
name = "Test Space for Detection Rules"
4750+
description = "Space for testing detection rules"
4751+
}
4752+
4753+
resource "elasticstack_kibana_security_detection_rule" "test" {
4754+
space_id = elasticstack_kibana_space.test.space_id
4755+
name = "%s"
4756+
type = "query"
4757+
query = "*:*"
4758+
language = "kuery"
4759+
enabled = true
4760+
description = "Minimal test query security detection rule in custom space"
4761+
severity = "low"
4762+
risk_score = 21
4763+
from = "now-6m"
4764+
to = "now"
4765+
interval = "5m"
4766+
index = ["logs-*"]
4767+
}
4768+
`, spaceID, name)
4769+
}
4770+
4771+
func testAccSecurityDetectionRuleConfig_queryMinimalWithSpaceUpdate(name, spaceID string) string {
4772+
return fmt.Sprintf(`
4773+
provider "elasticstack" {
4774+
kibana {}
4775+
}
4776+
4777+
resource "elasticstack_kibana_space" "test" {
4778+
space_id = "%s"
4779+
name = "Test Space for Detection Rules"
4780+
description = "Space for testing detection rules"
4781+
}
4782+
4783+
resource "elasticstack_kibana_security_detection_rule" "test" {
4784+
space_id = elasticstack_kibana_space.test.space_id
4785+
name = "%s"
4786+
type = "query"
4787+
query = "event.category:authentication"
4788+
language = "kuery"
4789+
enabled = false
4790+
description = "Updated minimal test query security detection rule in custom space"
4791+
severity = "medium"
4792+
risk_score = 55
4793+
from = "now-12m"
4794+
to = "now"
4795+
interval = "10m"
4796+
index = ["logs-*", "winlogbeat-*"]
4797+
}
4798+
`, spaceID, name)
4799+
}
4800+
46284801
func testAccSecurityDetectionRuleConfig_queryWithMitreThreat(name string) string {
46294802
return fmt.Sprintf(`
46304803
provider "elasticstack" {

internal/kibana/security_detection_rule/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (r *securityDetectionRuleResource) Create(ctx context.Context, req resource
3535
}
3636

3737
// Create the rule
38-
response, err := kbClient.API.CreateRuleWithResponse(ctx, createProps)
38+
response, err := kbClient.API.CreateRuleWithResponse(ctx, data.SpaceId.ValueString(), createProps)
3939
if err != nil {
4040
resp.Diagnostics.AddError(
4141
"Error creating security detection rule",

internal/kibana/security_detection_rule/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func (r *securityDetectionRuleResource) Delete(ctx context.Context, req resource
4646
Id: &ruleObjectId,
4747
}
4848

49-
response, err := kbClient.API.DeleteRuleWithResponse(ctx, params)
49+
response, err := kbClient.API.DeleteRuleWithResponse(ctx, data.SpaceId.ValueString(), params)
5050
if err != nil {
5151
resp.Diagnostics.AddError(
5252
"Error deleting security detection rule",

internal/kibana/security_detection_rule/read.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (r *securityDetectionRuleResource) read(ctx context.Context, resourceId, sp
7474
Id: &ruleObjectId,
7575
}
7676

77-
response, err := kbClient.API.ReadRuleWithResponse(ctx, params)
77+
response, err := kbClient.API.ReadRuleWithResponse(ctx, spaceId, params)
7878
if err != nil {
7979
diags.AddError(
8080
"Error reading security detection rule",

internal/kibana/security_detection_rule/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (r *securityDetectionRuleResource) Update(ctx context.Context, req resource
3535
}
3636

3737
// Update the rule
38-
response, err := kbClient.API.UpdateRuleWithResponse(ctx, updateProps)
38+
response, err := kbClient.API.UpdateRuleWithResponse(ctx, data.SpaceId.ValueString(), updateProps)
3939
if err != nil {
4040
resp.Diagnostics.AddError(
4141
"Error updating security detection rule",

0 commit comments

Comments
 (0)