Skip to content

Commit ee44260

Browse files
Merge pull request #607 from SumoLogic/DET-383-insights-with-dynamic-severity
DET-383: Dynamic severity for Custom Insights
2 parents 8a6b441 + 0580f17 commit ee44260

File tree

5 files changed

+185
-55
lines changed

5 files changed

+185
-55
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 2.28.1 (Unreleased)
22

3+
ENHANCEMENTS:
4+
* Added support for `dynamic_severity` for the CSE Custom Insight. (GH-607)
5+
36
BUG FIXES:
47
* Minor fixes enabling proper resource import for CSE Rules (`severity`, `severity_mapping`, `aggregation_functions`). (GH-606)
58

sumologic/resource_sumologic_cse_custom_insight.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ func resourceSumologicCSECustomInsight() *schema.Resource {
4545
Required: true,
4646
ValidateFunc: validation.StringInSlice([]string{"HIGH", "MEDIUM", "LOW", "CRITICAL"}, false),
4747
},
48+
"dynamic_severity": {
49+
Type: schema.TypeList,
50+
Optional: true,
51+
Elem: &schema.Resource{
52+
Schema: map[string]*schema.Schema{
53+
"minimum_signal_severity": {
54+
Type: schema.TypeInt,
55+
Required: true,
56+
},
57+
"insight_severity": {
58+
Type: schema.TypeString,
59+
Required: true,
60+
ValidateFunc: validation.StringInSlice([]string{"HIGH", "MEDIUM", "LOW", "CRITICAL"}, false),
61+
},
62+
},
63+
},
64+
},
4865
"signal_names": {
4966
Type: schema.TypeList,
5067
Optional: true,
@@ -86,6 +103,7 @@ func resourceSumologicCSECustomInsightRead(d *schema.ResourceData, meta interfac
86103
d.Set("ordered", CSECustomInsightGet.Ordered)
87104
d.Set("rule_ids", CSECustomInsightGet.RuleIds)
88105
d.Set("severity", CSECustomInsightGet.Severity)
106+
d.Set("dynamic_severity", dynamicSeverityArrayToResource(CSECustomInsightGet.DynamicSeverity))
89107
d.Set("signal_names", CSECustomInsightGet.SignalNames)
90108
d.Set("tags", CSECustomInsightGet.Tags)
91109

@@ -103,14 +121,15 @@ func resourceSumologicCSECustomInsightCreate(d *schema.ResourceData, meta interf
103121

104122
if d.Id() == "" {
105123
id, err := c.CreateCSECustomInsight(CSECustomInsight{
106-
Description: d.Get("description").(string),
107-
Enabled: d.Get("enabled").(bool),
108-
RuleIds: resourceToStringArray(d.Get("rule_ids").([]interface{})),
109-
Name: d.Get("name").(string),
110-
Ordered: d.Get("ordered").(bool),
111-
Severity: d.Get("severity").(string),
112-
SignalNames: resourceToStringArray(d.Get("signal_names").([]interface{})),
113-
Tags: resourceToStringArray(d.Get("tags").([]interface{})),
124+
Description: d.Get("description").(string),
125+
Enabled: d.Get("enabled").(bool),
126+
RuleIds: resourceToStringArray(d.Get("rule_ids").([]interface{})),
127+
Name: d.Get("name").(string),
128+
Ordered: d.Get("ordered").(bool),
129+
Severity: d.Get("severity").(string),
130+
DynamicSeverity: resourceToDynamicSeverityArray(d.Get("dynamic_severity").([]interface{})),
131+
SignalNames: resourceToStringArray(d.Get("signal_names").([]interface{})),
132+
Tags: resourceToStringArray(d.Get("tags").([]interface{})),
114133
})
115134

116135
if err != nil {
@@ -143,14 +162,41 @@ func resourceToCSECustomInsight(d *schema.ResourceData) (CSECustomInsight, error
143162
}
144163

145164
return CSECustomInsight{
146-
ID: id,
147-
Description: d.Get("description").(string),
148-
Enabled: d.Get("enabled").(bool),
149-
RuleIds: resourceToStringArray(d.Get("rule_ids").([]interface{})),
150-
Name: d.Get("name").(string),
151-
Ordered: d.Get("ordered").(bool),
152-
Severity: d.Get("severity").(string),
153-
SignalNames: resourceToStringArray(d.Get("signal_names").([]interface{})),
154-
Tags: resourceToStringArray(d.Get("tags").([]interface{})),
165+
ID: id,
166+
Description: d.Get("description").(string),
167+
Enabled: d.Get("enabled").(bool),
168+
RuleIds: resourceToStringArray(d.Get("rule_ids").([]interface{})),
169+
Name: d.Get("name").(string),
170+
Ordered: d.Get("ordered").(bool),
171+
Severity: d.Get("severity").(string),
172+
DynamicSeverity: resourceToDynamicSeverityArray(d.Get("dynamic_severity").([]interface{})),
173+
SignalNames: resourceToStringArray(d.Get("signal_names").([]interface{})),
174+
Tags: resourceToStringArray(d.Get("tags").([]interface{})),
155175
}, nil
156176
}
177+
178+
func resourceToDynamicSeverityArray(resourceDynamicSeverity []interface{}) []DynamicSeverity {
179+
result := make([]DynamicSeverity, len(resourceDynamicSeverity))
180+
181+
for i, resourceDynamicSeverity := range resourceDynamicSeverity {
182+
result[i] = DynamicSeverity{
183+
MinimumSignalSeverity: resourceDynamicSeverity.(map[string]interface{})["minimum_signal_severity"].(int),
184+
InsightSeverity: resourceDynamicSeverity.(map[string]interface{})["insight_severity"].(string),
185+
}
186+
}
187+
188+
return result
189+
}
190+
191+
func dynamicSeverityArrayToResource(dynamicSeverities []DynamicSeverity) []map[string]interface{} {
192+
result := make([]map[string]interface{}, len(dynamicSeverities))
193+
194+
for i, dynamicSeverity := range dynamicSeverities {
195+
result[i] = map[string]interface{}{
196+
"minimum_signal_severity": dynamicSeverity.MinimumSignalSeverity,
197+
"insight_severity": dynamicSeverity.InsightSeverity,
198+
}
199+
}
200+
201+
return result
202+
}

sumologic/resource_sumologic_cse_custom_insight_test.go

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
88
"github.com/hashicorp/terraform-plugin-sdk/terraform"
9+
"github.com/stretchr/testify/assert"
910
)
1011

1112
func TestAccSumologicCSECustomInsight_createAndUpdate(t *testing.T) {
@@ -35,7 +36,7 @@ func TestAccSumologicCSECustomInsight_createAndUpdate(t *testing.T) {
3536
ordered, name, severity, signalName1, signalName2, tag),
3637
Check: resource.ComposeTestCheckFunc(
3738
testCheckCSECustomInsightExists(resourceName, &CustomInsight),
38-
testCheckCustomInsightValues(&CustomInsight, description, enabled,
39+
testCheckCustomInsightValues(t, &CustomInsight, description, enabled,
3940
ordered, name, severity, signalName1, signalName2, tag),
4041
resource.TestCheckResourceAttrSet(resourceName, "id"),
4142
),
@@ -46,12 +47,62 @@ func TestAccSumologicCSECustomInsight_createAndUpdate(t *testing.T) {
4647
signalName2, tag),
4748
Check: resource.ComposeTestCheckFunc(
4849
testCheckCSECustomInsightExists(resourceName, &CustomInsight),
49-
testCheckCustomInsightValues(&CustomInsight, description, enabled,
50+
testCheckCustomInsightValues(t, &CustomInsight, description, enabled,
5051
ordered, nameUpdated, severityUpdated, signalName1,
5152
signalName2, tag),
5253
resource.TestCheckResourceAttrSet(resourceName, "id"),
5354
),
5455
},
56+
{
57+
ResourceName: resourceName,
58+
ImportState: true,
59+
ImportStateVerify: true,
60+
},
61+
},
62+
})
63+
}
64+
65+
func TestAccSumologicCSECustomInsight_createAndUpdateWithDynamicSeverity(t *testing.T) {
66+
SkipCseTest(t)
67+
68+
var CustomInsight CSECustomInsight
69+
minimumSignalSeverity1 := 5
70+
dynamicSeverity1 := "MEDIUM"
71+
minimumSignalSeverity2 := 8
72+
dynamicSeverity2 := "HIGH"
73+
74+
updatedMinimumSignalSeverity2 := 9
75+
updatedDynamicSeverity2 := "CRITICAL"
76+
77+
resourceName := "sumologic_cse_custom_insight.custom_insight2"
78+
resource.Test(t, resource.TestCase{
79+
PreCheck: func() { testAccPreCheck(t) },
80+
Providers: testAccProviders,
81+
CheckDestroy: testAccCSECustomInsightDestroy,
82+
Steps: []resource.TestStep{
83+
{
84+
Config: testCreateCSECustomInsightConfigWithDynamicSeverity(minimumSignalSeverity1, dynamicSeverity1, minimumSignalSeverity2, dynamicSeverity2),
85+
Check: resource.ComposeTestCheckFunc(
86+
testCheckCSECustomInsightExists(resourceName, &CustomInsight),
87+
testCheckCustomInsightDynamicSeverity(t, &CustomInsight,
88+
minimumSignalSeverity1, dynamicSeverity1, minimumSignalSeverity2, dynamicSeverity2),
89+
resource.TestCheckResourceAttrSet(resourceName, "id"),
90+
),
91+
},
92+
{
93+
Config: testCreateCSECustomInsightConfigWithDynamicSeverity(minimumSignalSeverity1, dynamicSeverity1, updatedMinimumSignalSeverity2, updatedDynamicSeverity2),
94+
Check: resource.ComposeTestCheckFunc(
95+
testCheckCSECustomInsightExists(resourceName, &CustomInsight),
96+
testCheckCustomInsightDynamicSeverity(t, &CustomInsight,
97+
minimumSignalSeverity1, dynamicSeverity1, updatedMinimumSignalSeverity2, updatedDynamicSeverity2),
98+
resource.TestCheckResourceAttrSet(resourceName, "id"),
99+
),
100+
},
101+
{
102+
ResourceName: resourceName,
103+
ImportState: true,
104+
ImportStateVerify: true,
105+
},
55106
},
56107
})
57108
}
@@ -96,6 +147,28 @@ resource "sumologic_cse_custom_insight" "custom_insight" {
96147
signalName2, tag)
97148
}
98149

150+
func testCreateCSECustomInsightConfigWithDynamicSeverity(
151+
minimumSignalSeverity1 int, dynamicSeverity1 string, minimumSignalSeverity2 int, dynamicSeverity2 string) string {
152+
return fmt.Sprintf(`
153+
resource "sumologic_cse_custom_insight" "custom_insight2" {
154+
description = "Dynamic severity insight"
155+
enabled = true
156+
ordered = true
157+
name = "Dynamic severity insight"
158+
severity = "LOW"
159+
dynamic_severity {
160+
minimum_signal_severity = %d
161+
insight_severity = "%s"
162+
}
163+
dynamic_severity {
164+
minimum_signal_severity = %d
165+
insight_severity = "%s"
166+
}
167+
tags = ["test tag"]
168+
}
169+
`, minimumSignalSeverity1, dynamicSeverity1, minimumSignalSeverity2, dynamicSeverity2)
170+
}
171+
99172
func testCheckCSECustomInsightExists(n string, CustomInsight *CSECustomInsight) resource.TestCheckFunc {
100173
return func(s *terraform.State) error {
101174
rs, ok := s.RootModule().Resources[n]
@@ -104,7 +177,7 @@ func testCheckCSECustomInsightExists(n string, CustomInsight *CSECustomInsight)
104177
}
105178

106179
if rs.Primary.ID == "" {
107-
return fmt.Errorf("chain rule ID is not set")
180+
return fmt.Errorf("CustomInsight ID is not set")
108181
}
109182

110183
c := testAccProvider.Meta().(*Client)
@@ -119,35 +192,30 @@ func testCheckCSECustomInsightExists(n string, CustomInsight *CSECustomInsight)
119192
}
120193
}
121194

122-
func testCheckCustomInsightValues(CustomInsight *CSECustomInsight, description string,
195+
func testCheckCustomInsightValues(t *testing.T, CustomInsight *CSECustomInsight, description string,
123196
enabled bool, ordered bool, name string, severity string, signalName1 string,
124197
signalName2 string, tag string) resource.TestCheckFunc {
125198
return func(s *terraform.State) error {
126-
if CustomInsight.Description != description {
127-
return fmt.Errorf("bad description, expected \"%s\", got %#v", description, CustomInsight.Description)
128-
}
129-
if CustomInsight.Enabled != enabled {
130-
return fmt.Errorf("bad enabled, expected \"%t\", got %#v", enabled, CustomInsight.Enabled)
131-
}
132-
if CustomInsight.Ordered != ordered {
133-
return fmt.Errorf("bad ordered, expected \"%t\", got %#v", ordered, CustomInsight.Ordered)
134-
}
135-
if CustomInsight.Name != name {
136-
return fmt.Errorf("bad name, expected \"%s\", got %#v", name, CustomInsight.Name)
137-
}
138-
if CustomInsight.Severity != severity {
139-
return fmt.Errorf("bad severity, expected \"%s\", got %#v", severity, CustomInsight.Severity)
140-
}
141-
if CustomInsight.SignalNames[0] != signalName1 {
142-
return fmt.Errorf("bad signalName1, expected \"%s\", got %#v", signalName1, CustomInsight.SignalNames[0])
143-
}
144-
if CustomInsight.SignalNames[1] != signalName2 {
145-
return fmt.Errorf("bad signalName2, expected \"%s\", got %#v", signalName2, CustomInsight.SignalNames[1])
146-
}
147-
if CustomInsight.Tags[0] != tag {
148-
return fmt.Errorf("bad tag, expected \"%s\", got %#v", tag, CustomInsight.Tags[0])
149-
}
199+
assert.Equal(t, description, CustomInsight.Description)
200+
assert.Equal(t, enabled, CustomInsight.Enabled)
201+
assert.Equal(t, ordered, CustomInsight.Ordered)
202+
assert.Equal(t, name, CustomInsight.Name)
203+
assert.Equal(t, severity, CustomInsight.Severity)
204+
assert.Equal(t, signalName1, CustomInsight.SignalNames[0])
205+
assert.Equal(t, signalName2, CustomInsight.SignalNames[1])
206+
assert.Equal(t, tag, CustomInsight.Tags[0])
207+
return nil
208+
}
209+
}
150210

211+
func testCheckCustomInsightDynamicSeverity(t *testing.T, CustomInsight *CSECustomInsight,
212+
minimumSignalSeverity1 int, dynamicSeverity1 string, minimumSignalSeverity2 int, dynamicSeverity2 string) resource.TestCheckFunc {
213+
214+
return func(s *terraform.State) error {
215+
assert.Equal(t, minimumSignalSeverity1, CustomInsight.DynamicSeverity[0].MinimumSignalSeverity)
216+
assert.Equal(t, dynamicSeverity1, CustomInsight.DynamicSeverity[0].InsightSeverity)
217+
assert.Equal(t, minimumSignalSeverity2, CustomInsight.DynamicSeverity[1].MinimumSignalSeverity)
218+
assert.Equal(t, dynamicSeverity2, CustomInsight.DynamicSeverity[1].InsightSeverity)
151219
return nil
152220
}
153221
}

sumologic/sumologic_cse_custom_insight.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,20 @@ type CSECustomInsightResponse struct {
7272
CSECustomInsight CSECustomInsight `json:"data"`
7373
}
7474

75+
type DynamicSeverity struct {
76+
MinimumSignalSeverity int `json:"minimumSignalSeverity"`
77+
InsightSeverity string `json:"insightSeverity"`
78+
}
79+
7580
type CSECustomInsight struct {
76-
ID string `json:"id,omitempty"`
77-
Description string `json:"description"`
78-
Enabled bool `json:"enabled"`
79-
Name string `json:"name"`
80-
Ordered bool `json:"ordered"`
81-
RuleIds []string `json:"ruleIds"`
82-
Severity string `json:"severity"`
83-
SignalNames []string `json:"signalNames"`
84-
Tags []string `json:"tags"`
81+
ID string `json:"id,omitempty"`
82+
Description string `json:"description"`
83+
Enabled bool `json:"enabled"`
84+
Name string `json:"name"`
85+
Ordered bool `json:"ordered"`
86+
RuleIds []string `json:"ruleIds"`
87+
Severity string `json:"severity"`
88+
DynamicSeverity []DynamicSeverity `json:"dynamicSeverity"`
89+
SignalNames []string `json:"signalNames"`
90+
Tags []string `json:"tags"`
8591
}

website/docs/r/cse_custom_insight.html.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ resource "sumologic_cse_custom_insight" "custom_insight" {
1717
name = "Custom Insight Example"
1818
rule_ids = ["MATCH-S00001", "THRESHOLD-U00005"]
1919
severity = "HIGH"
20+
dynamic_severity {
21+
minimum_signal_severity = 8
22+
insight_severity = "CRITICAL"
23+
}
2024
signal_names = ["Some Signal Name", "Wildcard Signal Name *"]
2125
tags = ["_mitreAttackTactic:TA0009"]
2226
}
@@ -31,7 +35,10 @@ The following arguments are supported:
3135
- `ordered` - (Required) Whether the signals matching the rule IDs/signal names must be in the same chronological order as they are listed in the Custom Insight
3236
- `name` - (Required) The name of the Custom Insight and the generated Insights
3337
- `rule_ids` - (Optional) The Rule IDs to match to generate an Insight (exactly one of rule_ids or signal_names must be specified)
34-
- `severity` - (Required) The severity of the generated Insights (HIGH, MEDIUM, or LOW)
38+
- `severity` - (Required) The severity of the generated Insights (CRITICAL, HIGH, MEDIUM, or LOW)
39+
- `dynamic_severity` - (Optional) The severity of the generated Insight that is based on the severity of the Signals that trigger the Insight.
40+
+ `minimum_signal_severity` - (Required) minimum Signal severity as the threshold for an Insight severity level
41+
+ `insight_severity` - (Required) The severity of the generated Insight (CRITICAL, HIGH, MEDIUM, or LOW)
3542
- `signal_names` - (Optional) The Signal names to match to generate an Insight (exactly one of rule_ids or signal_names must be specified)
3643
- `tags` - (Required) The tags of the generated Insights
3744

0 commit comments

Comments
 (0)