Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_sentinel_alert_rule_nrt, azurerm_sentinel_alert_rule_scheduled: support sentinel_entity_mapping #20230

Merged
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
38 changes: 38 additions & 0 deletions internal/services/sentinel/sentinel_alert_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,41 @@ func flattenAlertRuleFieldMapping(input *[]securityinsight.FieldMapping) []inter

return output
}

func expandAlertRuleSentinelEntityMapping(input []interface{}) *[]securityinsight.SentinelEntityMapping {
if len(input) == 0 {
return nil
}

result := make([]securityinsight.SentinelEntityMapping, 0)

for _, e := range input {
b := e.(map[string]interface{})
result = append(result, securityinsight.SentinelEntityMapping{
ColumnName: utils.String(b["column_name"].(string)),
})
}

return &result
}

func flattenAlertRuleSentinelEntityMapping(input *[]securityinsight.SentinelEntityMapping) []interface{} {
if input == nil {
return []interface{}{}
}

output := make([]interface{}, 0)

for _, e := range *input {
var columnName string
if e.ColumnName != nil {
columnName = *e.ColumnName
}

output = append(output, map[string]interface{}{
"column_name": columnName,
})
}

return output
}
30 changes: 30 additions & 0 deletions internal/services/sentinel/sentinel_alert_rule_nrt_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,20 @@ func resourceSentinelAlertRuleNrt() *pluginsdk.Resource {
},
},
},
"sentinel_entity_mapping": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 5,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"column_name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -373,8 +387,21 @@ func resourceSentinelAlertRuleNrtCreateUpdate(d *pluginsdk.ResourceData, meta in
if v, ok := d.GetOk("custom_details"); ok {
param.NrtAlertRuleProperties.CustomDetails = utils.ExpandMapStringPtrString(v.(map[string]interface{}))
}

entityMappingCount := 0
sentinelEntityMappingCount := 0
if v, ok := d.GetOk("entity_mapping"); ok {
param.NrtAlertRuleProperties.EntityMappings = expandAlertRuleEntityMapping(v.([]interface{}))
entityMappingCount = len(*param.NrtAlertRuleProperties.EntityMappings)
}
if v, ok := d.GetOk("sentinel_entity_mapping"); ok {
param.NrtAlertRuleProperties.SentinelEntitiesMappings = expandAlertRuleSentinelEntityMapping(v.([]interface{}))
sentinelEntityMappingCount = len(*param.NrtAlertRuleProperties.SentinelEntitiesMappings)
}

// the max number of `sentinel_entity_mapping` and `entity_mapping` together is 5
if entityMappingCount+sentinelEntityMappingCount > 5 {
return fmt.Errorf("`entity_mapping` and `sentinel_entity_mapping` together can't exceed 5")
}

// Service avoid concurrent update of this resource via checking the "etag" to guarantee it is the same value as last Read.
Expand Down Expand Up @@ -460,6 +487,9 @@ func resourceSentinelAlertRuleNrtRead(d *pluginsdk.ResourceData, meta interface{
if err := d.Set("entity_mapping", flattenAlertRuleEntityMapping(prop.EntityMappings)); err != nil {
return fmt.Errorf("setting `entity_mapping`: %+v", err)
}
if err := d.Set("sentinel_entity_mapping", flattenAlertRuleSentinelEntityMapping(prop.SentinelEntitiesMappings)); err != nil {
return fmt.Errorf("setting `sentinel_entity_mapping`: %+v", err)
}
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ resource "azurerm_sentinel_alert_rule_nrt" "test" {
column_name = "Computer"
}
}
sentinel_entity_mapping {
column_name = "Category"
}
entity_mapping {
entity_type = "IP"
field_mapping {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,20 @@ func resourceSentinelAlertRuleScheduled() *pluginsdk.Resource {
},
},
},
"sentinel_entity_mapping": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 5,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"column_name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -459,8 +473,21 @@ func resourceSentinelAlertRuleScheduledCreateUpdate(d *pluginsdk.ResourceData, m
if v, ok := d.GetOk("custom_details"); ok {
param.ScheduledAlertRuleProperties.CustomDetails = utils.ExpandMapStringPtrString(v.(map[string]interface{}))
}

entityMappingCount := 0
sentinelEntityMappingCount := 0
if v, ok := d.GetOk("entity_mapping"); ok {
param.ScheduledAlertRuleProperties.EntityMappings = expandAlertRuleEntityMapping(v.([]interface{}))
entityMappingCount = len(*param.ScheduledAlertRuleProperties.EntityMappings)
}
if v, ok := d.GetOk("sentinel_entity_mapping"); ok {
param.ScheduledAlertRuleProperties.SentinelEntitiesMappings = expandAlertRuleSentinelEntityMapping(v.([]interface{}))
sentinelEntityMappingCount = len(*param.ScheduledAlertRuleProperties.SentinelEntitiesMappings)
}

// the max number of `sentinel_entity_mapping` and `entity_mapping` together is 5
if entityMappingCount+sentinelEntityMappingCount > 5 {
return fmt.Errorf("`entity_mapping` and `sentinel_entity_mapping` together can't exceed 5")
}

if !d.IsNewResource() {
Expand Down Expand Up @@ -556,6 +583,9 @@ func resourceSentinelAlertRuleScheduledRead(d *pluginsdk.ResourceData, meta inte
if err := d.Set("entity_mapping", flattenAlertRuleEntityMapping(prop.EntityMappings)); err != nil {
return fmt.Errorf("setting `entity_mapping`: %+v", err)
}
if err := d.Set("sentinel_entity_mapping", flattenAlertRuleSentinelEntityMapping(prop.SentinelEntitiesMappings)); err != nil {
return fmt.Errorf("setting `sentinel_entity_mapping`: %+v", err)
}
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ resource "azurerm_sentinel_alert_rule_scheduled" "test" {
column_name = "Computer"
}
}
sentinel_entity_mapping {
column_name = "Category"
}
entity_mapping {
entity_type = "IP"
field_mapping {
Expand Down
10 changes: 10 additions & 0 deletions website/docs/r/sentinel_alert_rule_nrt.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ The following arguments are supported:

* `entity_mapping` - (Optional) A list of `entity_mapping` blocks as defined below.

* `sentinel_entity_mapping` - (Optional) A list of `sentinel_entity_mapping` blocks as defined below.

-> **NOTE:** `entity_mapping` and `sentinel_entity_mapping` together can't exceed 5.

* `incident` - (Optional) A `incident` block as defined below.

* `suppression_duration` - (Optional) If `suppression_enabled` is `true`, this is ISO 8601 timespan duration, which specifies the amount of time the query should stop running after alert is generated. Defaults to `PT5H`.
Expand Down Expand Up @@ -118,6 +122,12 @@ An `entity_mapping` block supports the following:

---

A `sentinel_entity_mapping` block supports the following:

* `column_name` - (Required) The column name to be mapped to the identifier.

---

A `field_mapping` block supports the following:

* `identifier` - (Required) The identifier of the entity.
Expand Down
10 changes: 10 additions & 0 deletions website/docs/r/sentinel_alert_rule_scheduled.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ The following arguments are supported:

* `suppression_enabled` - (Optional) Should the Sentinel Scheduled Alert Rulea stop running query after alert is generated? Defaults to `false`.

* `sentinel_entity_mapping` - (Optional) A list of `sentinel_entity_mapping` blocks as defined below.

-> **NOTE:** `entity_mapping` and `sentinel_entity_mapping` together can't exceed 5.

* `tactics` - (Optional) A list of categories of attacks by which to classify the rule. Possible values are `Collection`, `CommandAndControl`, `CredentialAccess`, `DefenseEvasion`, `Discovery`, `Execution`, `Exfiltration`, `ImpairProcessControl`, `InhibitResponseFunction`, `Impact`, `InitialAccess`, `LateralMovement`, `Persistence`, `PrivilegeEscalation`, `PreAttack`, `Reconnaissance` and `ResourceDevelopment`.

* `techniques` - (Optional) A list of techniques of attacks by which to classify the rule.
Expand Down Expand Up @@ -132,6 +136,12 @@ An `entity_mapping` block supports the following:

---

A `sentinel_entity_mapping` block supports the following:

* `column_name` - (Required) The column name to be mapped to the identifier.

---

A `event_grouping` block supports the following:

* `aggregation_method` - (Required) The aggregation type of grouping the events. Possible values are `AlertPerResult` and `SingleAlert`.
Expand Down