Skip to content

Commit 32d0255

Browse files
Copilotnick-benoit
andcommitted
Create basic structure for Kibana Security Detection Rules resource
Co-authored-by: nick-benoit <[email protected]>
1 parent 0b2e3eb commit 32d0255

File tree

9 files changed

+396
-0
lines changed

9 files changed

+396
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package detection_rule_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/elastic/terraform-provider-elasticstack/internal/acctest"
7+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
8+
)
9+
10+
func TestAccResourceKibanaSecurityDetectionRule(t *testing.T) {
11+
resource.Test(t, resource.TestCase{
12+
PreCheck: func() { acctest.PreCheck(t) },
13+
ProtoV6ProviderFactories: acctest.Providers,
14+
Steps: []resource.TestStep{
15+
{
16+
Config: testAccResourceKibanaSecurityDetectionRuleCreate(),
17+
Check: resource.ComposeTestCheckFunc(
18+
resource.TestCheckResourceAttr("elasticstack_kibana_security_detection_rule.test", "name", "Test Detection Rule"),
19+
resource.TestCheckResourceAttr("elasticstack_kibana_security_detection_rule.test", "description", "Test security detection rule"),
20+
resource.TestCheckResourceAttr("elasticstack_kibana_security_detection_rule.test", "type", "query"),
21+
resource.TestCheckResourceAttr("elasticstack_kibana_security_detection_rule.test", "severity", "medium"),
22+
resource.TestCheckResourceAttr("elasticstack_kibana_security_detection_rule.test", "enabled", "true"),
23+
),
24+
},
25+
},
26+
})
27+
}
28+
29+
func testAccResourceKibanaSecurityDetectionRuleCreate() string {
30+
return `
31+
provider "elasticstack" {
32+
kibana {}
33+
}
34+
35+
resource "elasticstack_kibana_security_detection_rule" "test" {
36+
name = "Test Detection Rule"
37+
description = "Test security detection rule"
38+
type = "query"
39+
query = "*:*"
40+
language = "kuery"
41+
severity = "medium"
42+
enabled = true
43+
tags = ["test"]
44+
interval = "5m"
45+
from = "now-6m"
46+
to = "now"
47+
}`
48+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package detection_rule
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/resource"
7+
)
8+
9+
func (r *securityDetectionRuleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
10+
var data SecurityDetectionRuleData
11+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
12+
if resp.Diagnostics.HasError() {
13+
return
14+
}
15+
16+
// TODO: Implement actual API call to create security detection rule
17+
// For now, just set a placeholder ID
18+
data.Id = data.RuleId
19+
if data.Id.IsNull() || data.Id.IsUnknown() {
20+
// Generate a UUID if rule_id is not provided
21+
data.Id = data.Name // Placeholder - should generate proper UUID
22+
data.RuleId = data.Id
23+
}
24+
25+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
26+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package detection_rule
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/resource"
7+
)
8+
9+
func (r *securityDetectionRuleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
10+
var data SecurityDetectionRuleData
11+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
12+
if resp.Diagnostics.HasError() {
13+
return
14+
}
15+
16+
// TODO: Implement actual API call to delete security detection rule
17+
// For now, just remove from state
18+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package detection_rule
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-framework/types"
5+
)
6+
7+
type SecurityDetectionRuleData struct {
8+
Id types.String `tfsdk:"id"`
9+
KibanaConnection types.List `tfsdk:"kibana_connection"`
10+
SpaceId types.String `tfsdk:"space_id"`
11+
RuleId types.String `tfsdk:"rule_id"`
12+
Name types.String `tfsdk:"name"`
13+
Description types.String `tfsdk:"description"`
14+
Type types.String `tfsdk:"type"`
15+
Query types.String `tfsdk:"query"`
16+
Language types.String `tfsdk:"language"`
17+
Index types.List `tfsdk:"index"`
18+
Severity types.String `tfsdk:"severity"`
19+
Risk types.Int64 `tfsdk:"risk"`
20+
Enabled types.Bool `tfsdk:"enabled"`
21+
Tags types.List `tfsdk:"tags"`
22+
From types.String `tfsdk:"from"`
23+
To types.String `tfsdk:"to"`
24+
Interval types.String `tfsdk:"interval"`
25+
Meta types.String `tfsdk:"meta"`
26+
Author types.List `tfsdk:"author"`
27+
License types.String `tfsdk:"license"`
28+
RuleNameOverride types.String `tfsdk:"rule_name_override"`
29+
TimestampOverride types.String `tfsdk:"timestamp_override"`
30+
Note types.String `tfsdk:"note"`
31+
References types.List `tfsdk:"references"`
32+
FalsePositives types.List `tfsdk:"false_positives"`
33+
ExceptionsList types.List `tfsdk:"exceptions_list"`
34+
Version types.Int64 `tfsdk:"version"`
35+
MaxSignals types.Int64 `tfsdk:"max_signals"`
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package detection_rule
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/resource"
7+
)
8+
9+
func (r *securityDetectionRuleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
10+
var data SecurityDetectionRuleData
11+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
12+
if resp.Diagnostics.HasError() {
13+
return
14+
}
15+
16+
// TODO: Implement actual API call to read security detection rule
17+
// For now, just keep the current state
18+
19+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
20+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package detection_rule
2+
3+
import (
4+
"context"
5+
6+
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
7+
"github.com/hashicorp/terraform-plugin-framework/resource"
8+
)
9+
10+
func NewSecurityDetectionRuleResource() resource.Resource {
11+
return &securityDetectionRuleResource{}
12+
}
13+
14+
type securityDetectionRuleResource struct {
15+
client *clients.ApiClient
16+
}
17+
18+
func (r *securityDetectionRuleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
19+
resp.TypeName = req.ProviderTypeName + "_kibana_security_detection_rule"
20+
}
21+
22+
func (r *securityDetectionRuleResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
23+
client, diags := clients.ConvertProviderData(req.ProviderData)
24+
resp.Diagnostics.Append(diags...)
25+
r.client = client
26+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package detection_rule
2+
3+
import (
4+
"context"
5+
"regexp"
6+
7+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
8+
"github.com/hashicorp/terraform-plugin-framework/attr"
9+
"github.com/hashicorp/terraform-plugin-framework/resource"
10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
12+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
13+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
18+
"github.com/hashicorp/terraform-plugin-framework/types"
19+
20+
providerschema "github.com/elastic/terraform-provider-elasticstack/internal/schema"
21+
)
22+
23+
func (r *securityDetectionRuleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
24+
resp.Schema = GetSchema()
25+
}
26+
27+
func GetSchema() schema.Schema {
28+
return schema.Schema{
29+
MarkdownDescription: "Creates or updates a Kibana security detection rule. See https://www.elastic.co/guide/en/security/current/rules-api-create.html",
30+
Blocks: map[string]schema.Block{
31+
"kibana_connection": providerschema.GetKbFWConnectionBlock(),
32+
},
33+
Attributes: map[string]schema.Attribute{
34+
"id": schema.StringAttribute{
35+
MarkdownDescription: "Internal identifier of the resource",
36+
Computed: true,
37+
},
38+
"space_id": schema.StringAttribute{
39+
MarkdownDescription: "An identifier for the space. If space_id is not provided, the default space is used.",
40+
Optional: true,
41+
Computed: true,
42+
Default: stringdefault.StaticString("default"),
43+
PlanModifiers: []planmodifier.String{
44+
stringplanmodifier.RequiresReplace(),
45+
},
46+
},
47+
"rule_id": schema.StringAttribute{
48+
MarkdownDescription: "The identifier for the rule. If not provided, an ID is randomly generated.",
49+
Optional: true,
50+
Computed: true,
51+
PlanModifiers: []planmodifier.String{
52+
stringplanmodifier.UseStateForUnknown(),
53+
stringplanmodifier.RequiresReplace(),
54+
},
55+
},
56+
"name": schema.StringAttribute{
57+
MarkdownDescription: "The name of the detection rule.",
58+
Required: true,
59+
},
60+
"description": schema.StringAttribute{
61+
MarkdownDescription: "The description of the detection rule.",
62+
Required: true,
63+
},
64+
"type": schema.StringAttribute{
65+
MarkdownDescription: "The rule type. Valid values are: eql, query, machine_learning, threshold, threat_match, new_terms.",
66+
Required: true,
67+
Validators: []validator.String{
68+
stringvalidator.OneOf("eql", "query", "machine_learning", "threshold", "threat_match", "new_terms"),
69+
},
70+
},
71+
"query": schema.StringAttribute{
72+
MarkdownDescription: "The query that the rule will use to generate alerts.",
73+
Optional: true,
74+
},
75+
"language": schema.StringAttribute{
76+
MarkdownDescription: "The query language. Valid values are: kuery, lucene, eql.",
77+
Optional: true,
78+
Computed: true,
79+
Default: stringdefault.StaticString("kuery"),
80+
Validators: []validator.String{
81+
stringvalidator.OneOf("kuery", "lucene", "eql"),
82+
},
83+
},
84+
"index": schema.ListAttribute{
85+
MarkdownDescription: "A list of index patterns to search.",
86+
ElementType: types.StringType,
87+
Optional: true,
88+
Computed: true,
89+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{types.StringValue("*")})),
90+
},
91+
"severity": schema.StringAttribute{
92+
MarkdownDescription: "The severity of the rule. Valid values are: low, medium, high, critical.",
93+
Required: true,
94+
Validators: []validator.String{
95+
stringvalidator.OneOf("low", "medium", "high", "critical"),
96+
},
97+
},
98+
"risk": schema.Int64Attribute{
99+
MarkdownDescription: "A numerical representation of the alert's severity from 1-100.",
100+
Optional: true,
101+
Computed: true,
102+
Default: int64default.StaticInt64(21),
103+
},
104+
"enabled": schema.BoolAttribute{
105+
MarkdownDescription: "Determines whether the rule is enabled.",
106+
Optional: true,
107+
Computed: true,
108+
Default: booldefault.StaticBool(true),
109+
},
110+
"tags": schema.ListAttribute{
111+
MarkdownDescription: "String array containing words and phrases to help categorize, filter, and search rules.",
112+
ElementType: types.StringType,
113+
Optional: true,
114+
Computed: true,
115+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
116+
},
117+
"from": schema.StringAttribute{
118+
MarkdownDescription: "Time from which data is analyzed each time the rule executes, using date math syntax.",
119+
Optional: true,
120+
Computed: true,
121+
Default: stringdefault.StaticString("now-6m"),
122+
},
123+
"to": schema.StringAttribute{
124+
MarkdownDescription: "Time to which data is analyzed each time the rule executes, using date math syntax.",
125+
Optional: true,
126+
Computed: true,
127+
Default: stringdefault.StaticString("now"),
128+
},
129+
"interval": schema.StringAttribute{
130+
MarkdownDescription: "How often the rule executes.",
131+
Optional: true,
132+
Computed: true,
133+
Default: stringdefault.StaticString("5m"),
134+
Validators: []validator.String{
135+
stringvalidator.RegexMatches(regexp.MustCompile(`^\d+[smhd]$`), "must be a valid duration (e.g., '5m', '1h')"),
136+
},
137+
},
138+
"meta": schema.StringAttribute{
139+
MarkdownDescription: "Optional metadata about the rule as a JSON string.",
140+
Optional: true,
141+
},
142+
"author": schema.ListAttribute{
143+
MarkdownDescription: "String array containing the rule's author(s).",
144+
ElementType: types.StringType,
145+
Optional: true,
146+
Computed: true,
147+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
148+
},
149+
"license": schema.StringAttribute{
150+
MarkdownDescription: "The rule's license.",
151+
Optional: true,
152+
},
153+
"rule_name_override": schema.StringAttribute{
154+
MarkdownDescription: "Sets the source field for the alert's rule name.",
155+
Optional: true,
156+
},
157+
"timestamp_override": schema.StringAttribute{
158+
MarkdownDescription: "Sets the time field used to query indices.",
159+
Optional: true,
160+
},
161+
"note": schema.StringAttribute{
162+
MarkdownDescription: "Notes to help investigate alerts produced by the rule.",
163+
Optional: true,
164+
},
165+
"references": schema.ListAttribute{
166+
MarkdownDescription: "String array containing notes about or references to relevant information about the rule.",
167+
ElementType: types.StringType,
168+
Optional: true,
169+
Computed: true,
170+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
171+
},
172+
"false_positives": schema.ListAttribute{
173+
MarkdownDescription: "String array describing common reasons why the rule may issue false-positive alerts.",
174+
ElementType: types.StringType,
175+
Optional: true,
176+
Computed: true,
177+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
178+
},
179+
"exceptions_list": schema.ListAttribute{
180+
MarkdownDescription: "List of exceptions that prevent alerts from being generated.",
181+
ElementType: types.StringType,
182+
Optional: true,
183+
Computed: true,
184+
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
185+
},
186+
"version": schema.Int64Attribute{
187+
MarkdownDescription: "The rule's version number.",
188+
Optional: true,
189+
Computed: true,
190+
Default: int64default.StaticInt64(1),
191+
},
192+
"max_signals": schema.Int64Attribute{
193+
MarkdownDescription: "Maximum number of alerts the rule can produce during a single execution.",
194+
Optional: true,
195+
Computed: true,
196+
Default: int64default.StaticInt64(100),
197+
},
198+
},
199+
}
200+
}

0 commit comments

Comments
 (0)