Skip to content

Commit 443d1e0

Browse files
committed
[datadog_security_monitoring_default_rule] Prevent users from overriding default rule query fields, allowing only custom_query_extension to be set
1 parent 4ceeba3 commit 443d1e0

3 files changed

Lines changed: 495 additions & 511 deletions

datadog/resource_datadog_security_monitoring_default_rule.go

Lines changed: 21 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,12 @@ func resourceDatadogSecurityMonitoringDefaultRule() *schema.Resource {
8383
},
8484
},
8585
"aggregation": {
86-
Type: schema.TypeString,
87-
ValidateDiagFunc: validators.ValidateEnumValue(datadogV2.NewSecurityMonitoringRuleQueryAggregationFromValue),
88-
Optional: true,
89-
Computed: true,
90-
Description: "The aggregation type. For Signal Correlation rules, it must be event_count.",
86+
Type: schema.TypeString,
87+
Computed: true,
88+
Description: "The aggregation type. For Signal Correlation rules, it must be event_count.",
9189
},
9290
"distinct_fields": {
9391
Type: schema.TypeList,
94-
Optional: true,
9592
Computed: true,
9693
Description: "Field for which the cardinality is measured. Sent as an array.",
9794
Elem: &schema.Schema{
@@ -101,7 +98,6 @@ func resourceDatadogSecurityMonitoringDefaultRule() *schema.Resource {
10198
},
10299
"group_by_fields": {
103100
Type: schema.TypeList,
104-
Optional: true,
105101
Computed: true,
106102
Description: "Fields to group by.",
107103
Elem: &schema.Schema{
@@ -111,40 +107,33 @@ func resourceDatadogSecurityMonitoringDefaultRule() *schema.Resource {
111107
},
112108
"has_optional_group_by_fields": {
113109
Type: schema.TypeBool,
114-
Optional: true,
115110
Computed: true,
116111
Description: "When false, events without a group-by value are ignored by the rule. When true, events with missing group-by fields are processed with `N/A`, replacing the missing values.",
117112
},
118113
"data_source": {
119-
Type: schema.TypeString,
120-
ValidateDiagFunc: validators.ValidateEnumValue(datadogV2.NewSecurityMonitoringStandardDataSourceFromValue),
121-
Optional: true,
122-
Computed: true,
123-
Description: "Source of events.",
114+
Type: schema.TypeString,
115+
Computed: true,
116+
Description: "Source of events.",
124117
},
125118
"metric": {
126119
Type: schema.TypeString,
127120
Deprecated: "Configure `metrics` instead. This attribute will be removed in the next major version of the provider.",
128-
Optional: true,
129121
Computed: true,
130122
Description: "The target field to aggregate over when using the `sum`, `max`, or `geo_data` aggregations.",
131123
},
132124
"metrics": {
133125
Type: schema.TypeList,
134126
Computed: true,
135-
Optional: true,
136127
Description: "Group of target fields to aggregate over when using the `sum`, `max`, `geo_data`, or `new_value` aggregations. The `sum`, `max`, and `geo_data` aggregations only accept one value in this list, whereas the `new_value` aggregation accepts up to five values.",
137128
Elem: &schema.Schema{Type: schema.TypeString},
138129
},
139130
"name": {
140131
Type: schema.TypeString,
141-
Optional: true,
142132
Computed: true,
143133
Description: "Name of the query. Not compatible with `new_value` aggregations.",
144134
},
145135
"query": {
146136
Type: schema.TypeString,
147-
Optional: true,
148137
Computed: true,
149138
Description: "Query to run on logs.",
150139
},
@@ -506,21 +495,24 @@ func buildSecMonDefaultRuleUpdatePayload(currentState *datadogV2.SecurityMonitor
506495
v, ok = d.GetOk("query")
507496
if ok && v != "" {
508497
tfQueries := v.([]interface{})
509-
payloadQueries := make([]datadogV2.SecurityMonitoringRuleQuery, len(tfQueries))
510-
for idx, tfQuery := range tfQueries {
511-
// For default rules, merge with existing query to preserve unspecified fields
512-
var existingQuery *datadogV2.SecurityMonitoringStandardRuleQuery
513-
if idx < len(currentState.GetQueries()) {
514-
existingQuery = &currentState.GetQueries()[idx]
498+
currentQueries := currentState.GetQueries()
499+
payloadQueries := make([]datadogV2.SecurityMonitoringRuleQuery, len(currentQueries))
500+
for idx, existingQuery := range currentQueries {
501+
// Start from the current API query; only custom_query_extension is user-configurable.
502+
payloadQuery := existingQuery
503+
if idx < len(tfQueries) {
504+
tfQuery := tfQueries[idx].(map[string]interface{})
505+
if v, ok := tfQuery["custom_query_extension"]; ok {
506+
cqe := v.(string)
507+
if cqe != existingQuery.GetCustomQueryExtension() {
508+
shouldUpdate = true
509+
}
510+
payloadQuery.SetCustomQueryExtension(cqe)
511+
}
515512
}
516-
payloadQueries[idx] = *buildUpdateDefaultRuleQuery(tfQuery, existingQuery)
513+
payloadQueries[idx] = datadogV2.SecurityMonitoringStandardRuleQueryAsSecurityMonitoringRuleQuery(&payloadQuery)
517514
}
518515
payload.SetQueries(payloadQueries)
519-
520-
// Compare queries including custom_query_extension
521-
if !compareQueries(currentState.GetQueries(), payloadQueries) {
522-
shouldUpdate = true
523-
}
524516
}
525517
}
526518

@@ -635,25 +627,6 @@ func buildSecMonDefaultRuleUpdatePayload(currentState *datadogV2.SecurityMonitor
635627
return &payload, shouldUpdate, nil
636628
}
637629

638-
// Helper function to compare queries including custom_query_extension
639-
func compareQueries(currentQueries []datadogV2.SecurityMonitoringStandardRuleQuery, payloadQueries []datadogV2.SecurityMonitoringRuleQuery) bool {
640-
if len(currentQueries) != len(payloadQueries) {
641-
return false
642-
}
643-
644-
// For now, we'll assume queries are different if they exist in the payload
645-
// This is a simplified approach - in a more complete implementation,
646-
// we would need to extract the standard query from the payload query
647-
// and compare each field individually
648-
649-
// Since we're building the payload from Terraform config and comparing with current state,
650-
// if there are any queries in the payload, we should check if they differ from current state
651-
// For simplicity, we'll return false (indicating a change) if there are queries in the payload
652-
// This ensures that any query changes are detected
653-
654-
return len(payloadQueries) == 0
655-
}
656-
657630
// Helper function to compare filters
658631
func compareFilters(currentFilters []datadogV2.SecurityMonitoringFilter, payloadFilters []datadogV2.SecurityMonitoringFilter) bool {
659632
if len(currentFilters) != len(payloadFilters) {
@@ -720,132 +693,3 @@ func resourceDatadogSecurityMonitoringDefaultRuleDelete(ctx context.Context, d *
720693
// no-op
721694
return nil
722695
}
723-
724-
// buildUpdateDefaultRuleQuery merges Terraform configuration with existing query state
725-
// to preserve fields that are not specified in the Terraform config
726-
func buildUpdateDefaultRuleQuery(tfQuery interface{}, existingQuery *datadogV2.SecurityMonitoringStandardRuleQuery) *datadogV2.SecurityMonitoringRuleQuery {
727-
query := tfQuery.(map[string]interface{})
728-
payloadQuery := datadogV2.SecurityMonitoringStandardRuleQuery{}
729-
730-
// Start with existing values if available
731-
if existingQuery != nil {
732-
// Preserve existing aggregation if not specified in TF config
733-
if _, ok := query["aggregation"]; !ok {
734-
if aggregation, exists := existingQuery.GetAggregationOk(); exists {
735-
payloadQuery.SetAggregation(*aggregation)
736-
}
737-
}
738-
739-
// Preserve existing group_by_fields if not specified in TF config
740-
if _, ok := query["group_by_fields"]; !ok {
741-
if groupByFields, exists := existingQuery.GetGroupByFieldsOk(); exists {
742-
payloadQuery.SetGroupByFields(*groupByFields)
743-
}
744-
}
745-
746-
if _, ok := query["has_optional_group_by_fields"]; !ok {
747-
if hasGbf, exists := existingQuery.GetHasOptionalGroupByFieldsOk(); exists {
748-
payloadQuery.SetHasOptionalGroupByFields(*hasGbf)
749-
}
750-
}
751-
752-
// Preserve existing distinct_fields if not specified in TF config
753-
if _, ok := query["distinct_fields"]; !ok {
754-
if distinctFields, exists := existingQuery.GetDistinctFieldsOk(); exists {
755-
payloadQuery.SetDistinctFields(*distinctFields)
756-
}
757-
}
758-
759-
// Preserve existing data_source if not specified in TF config
760-
if _, ok := query["data_source"]; !ok {
761-
if dataSource, exists := existingQuery.GetDataSourceOk(); exists {
762-
payloadQuery.SetDataSource(*dataSource)
763-
}
764-
}
765-
766-
// Preserve existing metric if not specified in TF config
767-
if _, ok := query["metric"]; !ok {
768-
if metric, exists := existingQuery.GetMetricOk(); exists {
769-
payloadQuery.SetMetric(*metric)
770-
}
771-
}
772-
773-
// Preserve existing metrics if not specified in TF config
774-
if _, ok := query["metrics"]; !ok {
775-
if metrics, exists := existingQuery.GetMetricsOk(); exists {
776-
payloadQuery.SetMetrics(*metrics)
777-
}
778-
}
779-
780-
// Preserve existing name if not specified in TF config
781-
if _, ok := query["name"]; !ok {
782-
if name, exists := existingQuery.GetNameOk(); exists {
783-
payloadQuery.SetName(*name)
784-
}
785-
}
786-
787-
// Preserve existing query if not specified in TF config
788-
if _, ok := query["query"]; !ok {
789-
if existingQueryStr, exists := existingQuery.GetQueryOk(); exists {
790-
payloadQuery.SetQuery(*existingQueryStr)
791-
}
792-
}
793-
794-
// Preserve existing custom_query_extension if not specified in TF config
795-
if _, ok := query["custom_query_extension"]; !ok {
796-
if customQueryExtension, exists := existingQuery.GetCustomQueryExtensionOk(); exists {
797-
payloadQuery.SetCustomQueryExtension(*customQueryExtension)
798-
}
799-
}
800-
}
801-
802-
// Override with values from Terraform config
803-
if v, ok := query["aggregation"]; ok {
804-
aggregation := datadogV2.SecurityMonitoringRuleQueryAggregation(v.(string))
805-
payloadQuery.SetAggregation(aggregation)
806-
}
807-
808-
if v, ok := query["group_by_fields"]; ok {
809-
payloadQuery.SetGroupByFields(parseStringArray(v.([]interface{})))
810-
}
811-
812-
if v, ok := query["has_optional_group_by_fields"]; ok {
813-
payloadQuery.SetHasOptionalGroupByFields(v.(bool))
814-
}
815-
816-
if v, ok := query["distinct_fields"]; ok {
817-
payloadQuery.SetDistinctFields(parseStringArray(v.([]interface{})))
818-
}
819-
820-
if v, ok := query["data_source"]; ok {
821-
dataSource := datadogV2.SecurityMonitoringStandardDataSource(v.(string))
822-
payloadQuery.SetDataSource(dataSource)
823-
}
824-
825-
if v, ok := query["metric"]; ok {
826-
metric := v.(string)
827-
payloadQuery.SetMetric(metric)
828-
}
829-
830-
if v, ok := query["metrics"]; ok {
831-
payloadQuery.SetMetrics(parseStringArray(v.([]interface{})))
832-
}
833-
834-
if v, ok := query["name"]; ok {
835-
name := v.(string)
836-
payloadQuery.SetName(name)
837-
}
838-
839-
if v, ok := query["query"]; ok {
840-
queryQuery := v.(string)
841-
payloadQuery.SetQuery(queryQuery)
842-
}
843-
844-
if v, ok := query["custom_query_extension"]; ok {
845-
queryExtension := v.(string)
846-
payloadQuery.SetCustomQueryExtension(queryExtension)
847-
}
848-
849-
standardRuleQuery := datadogV2.SecurityMonitoringStandardRuleQueryAsSecurityMonitoringRuleQuery(&payloadQuery)
850-
return &standardRuleQuery
851-
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2026-02-16T15:28:20.581792+01:00
1+
2026-03-17T13:36:26.398399+01:00

0 commit comments

Comments
 (0)