Skip to content
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
## [Unreleased]

- Add the `alerts_filter` field to the `actions` in the Create Rule API ([#774](https://github.com/elastic/terraform-provider-elasticstack/pull/774))
- Add the `alert_delay` field to the Create Rule API ([#715](https://github.com/elastic/terraform-provider-elasticstack/pull/715))
- Add support for data_stream `lifecycle` template settings ([#724](https://github.com/elastic/terraform-provider-elasticstack/pull/724))
- Fix a provider panic when `elasticstack_kibana_action_connector` reads a non-existant connector ([#729](https://github.com/elastic/terraform-provider-elasticstack/pull/729))
- Add support for `remote_indicies` to `elasticstack_elasticsearch_security_role` & `elasticstack_kibana_security_role` (#723)[https://github.com/elastic/terraform-provider-elasticstack/pull/723]
- Fix error handling in `elasticstack_kibana_import_saved_objects` ([#738](https://github.com/elastic/terraform-provider-elasticstack/pull/738))
- Remove `space_id` parameter from private locations to fix inconsistent state for `elasticstack_kibana_synthetics_private_location` `space_id` ([#733](https://github.com/elastic/terraform-provider-elasticstack/pull/733))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I genuinely like seeing other people have their editors configured to trim trailing whitepace.

- Remove `space_id` parameter from private locations to fix inconsistent state for `elasticstack_kibana_synthetics_private_location` `space_id` ([#733](https://github.com/elastic/terraform-provider-elasticstack/pull/733))
- Add the `Frequency` field to the Create Rule API ([#753](https://github.com/elastic/terraform-provider-elasticstack/pull/753))
- Prevent a provider panic when the repository referenced in an `elasticstack_elasticsearch_snapshot_repository` does not exist ([#758](https://github.com/elastic/terraform-provider-elasticstack/pull/758))
- Add support for `remote_indicies` to `elasticstack_elasticsearch_security_api_key` (#766)[https://github.com/elastic/terraform-provider-elasticstack/pull/766]
Expand Down
21 changes: 21 additions & 0 deletions docs/resources/kibana_alerting_rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,30 @@ Required:

Optional:

- `alerts_filter` (Block List, Max: 1) Conditions that affect whether the action runs. If you specify multiple conditions, all conditions must be met for the action to run. For example, if an alert occurs within the specified time frame and matches the query, the action runs. (see [below for nested schema](#nestedblock--actions--alerts_filter))
- `frequency` (Block List, Max: 1) The properties that affect how often actions are generated. If the rule type supports setting summary to true, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level. (see [below for nested schema](#nestedblock--actions--frequency))
- `group` (String) The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names.

<a id="nestedblock--actions--alerts_filter"></a>
### Nested Schema for `actions.alerts_filter`

Optional:

- `kql` (String) Defines a query filter that determines whether the action runs. Written in Kibana Query Language (KQL).
- `timeframe` (Block List, Max: 1) Defines a period that limits whether the action runs. (see [below for nested schema](#nestedblock--actions--alerts_filter--timeframe))

<a id="nestedblock--actions--alerts_filter--timeframe"></a>
### Nested Schema for `actions.alerts_filter.timeframe`

Required:

- `days` (List of Number) Defines the days of the week that the action can run, represented as an array of numbers. For example, 1 represents Monday. An empty array is equivalent to specifying all the days of the week.
- `hours_end` (String) Defines the range of time in a day that the action can run. The end of the time frame in 24-hour notation (hh:mm).
- `hours_start` (String) Defines the range of time in a day that the action can run. The start of the time frame in 24-hour notation (hh:mm).
- `timezone` (String) The ISO time zone for the hours values. Values such as UTC and UTC+1 also work but lack built-in daylight savings time support and are not recommended.



<a id="nestedblock--actions--frequency"></a>
### Nested Schema for `actions.frequency`

Expand Down
38 changes: 37 additions & 1 deletion internal/clients/kibana/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,28 @@ func ruleResponseToModel(spaceID string, res *alerting.RuleResponseProperties) *
if !alerting.IsNil(action.Frequency) {
frequency := unwrapOptionalField(action.Frequency)

a.Frequency = &models.AlertingRuleActionFrequency{
a.Frequency = &models.ActionFrequency{
Summary: frequency.Summary,
NotifyWhen: (string)(frequency.NotifyWhen),
Throttle: frequency.Throttle.Get(),
}
}

if !alerting.IsNil(action.AlertsFilter) {
filter := unwrapOptionalField(action.AlertsFilter)
timeframe := unwrapOptionalField(filter.Timeframe)

a.AlertsFilter = &models.ActionAlertsFilter{
Kql: *filter.Query.Kql,
Timeframe: models.AlertsFilterTimeframe{
Days: timeframe.Days,
Timezone: *timeframe.Timezone,
HoursStart: *timeframe.Hours.Start,
HoursEnd: *timeframe.Hours.End,
},
}
}

actions = append(actions, a)
}

Expand Down Expand Up @@ -100,6 +115,27 @@ func ruleActionsToActionsInner(ruleActions []models.AlertingRuleAction) []alerti
actionToAppend.Frequency = &frequency
}

if !alerting.IsNil(action.AlertsFilter) {
timeframe := action.AlertsFilter.Timeframe

filter := alerting.ActionsInnerAlertsFilter{
Query: &alerting.ActionsInnerAlertsFilterQuery{
Kql: &action.AlertsFilter.Kql,
Filters: []alerting.Filter{},
},
Timeframe: &alerting.ActionsInnerAlertsFilterTimeframe{
Timezone: &timeframe.Timezone,
Days: timeframe.Days,
Hours: &alerting.ActionsInnerAlertsFilterTimeframeHours{
Start: &timeframe.HoursStart,
End: &timeframe.HoursEnd,
},
},
}

actionToAppend.AlertsFilter = &filter
}

actions = append(actions, actionToAppend)
}
return actions
Expand Down
26 changes: 24 additions & 2 deletions internal/clients/kibana/alerting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ func Test_ruleResponseToModel(t *testing.T) {
NotifyWhen: "onThrottleInterval",
Throttle: *alerting.NewNullableString(utils.Pointer("10s")),
}),
AlertsFilter: utils.Pointer(alerting.ActionsInnerAlertsFilter{
Query: &alerting.ActionsInnerAlertsFilterQuery{
Kql: utils.Pointer("foobar"),
},
Timeframe: &alerting.ActionsInnerAlertsFilterTimeframe{
Days: []int32{3, 5, 7},
Timezone: utils.Pointer("UTC+1"),
Hours: &alerting.ActionsInnerAlertsFilterTimeframeHours{
Start: utils.Pointer("00:00"),
End: utils.Pointer("08:00"),
},
},
}),
},
{
Group: "group-2",
Expand Down Expand Up @@ -128,17 +141,26 @@ func Test_ruleResponseToModel(t *testing.T) {
Group: "group-1",
ID: "id",
Params: map[string]interface{}{},
Frequency: &models.AlertingRuleActionFrequency{
Frequency: &models.ActionFrequency{
Summary: true,
NotifyWhen: "onThrottleInterval",
Throttle: utils.Pointer("10s"),
},
AlertsFilter: &models.ActionAlertsFilter{
Kql: "foobar",
Timeframe: models.AlertsFilterTimeframe{
Days: []int32{3, 5, 7},
Timezone: "UTC+1",
HoursStart: "00:00",
HoursEnd: "08:00",
},
},
},
{
Group: "group-2",
ID: "id",
Params: map[string]interface{}{},
Frequency: &models.AlertingRuleActionFrequency{
Frequency: &models.ActionFrequency{
Summary: true,
NotifyWhen: "onActionGroupChange",
},
Expand Down
115 changes: 108 additions & 7 deletions internal/kibana/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

var alertDelayMinSupportedVersion = version.Must(version.NewVersion("8.13.0"))

// when notify_when and throttle became optional
var frequencyMinSupportedVersion = version.Must(version.NewVersion("8.6.0"))
var alertsFilterMinSupportedVersion = version.Must(version.NewVersion("8.9.0"))
var alertDelayMinSupportedVersion = version.Must(version.NewVersion("8.13.0"))

func ResourceAlertingRule() *schema.Resource {
apikeySchema := map[string]*schema.Schema{
Expand Down Expand Up @@ -125,6 +125,59 @@ func ResourceAlertingRule() *schema.Resource {
},
},
},
"alerts_filter": {
Description: "Conditions that affect whether the action runs. If you specify multiple conditions, all conditions must be met for the action to run. For example, if an alert occurs within the specified time frame and matches the query, the action runs.",
Type: schema.TypeList,
MinItems: 0,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"kql": {
Description: "Defines a query filter that determines whether the action runs. Written in Kibana Query Language (KQL).",
Type: schema.TypeString,
Optional: true,
},
"timeframe": {
Description: "Defines a period that limits whether the action runs.",
Type: schema.TypeList,
MinItems: 0,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"days": {
Description: "Defines the days of the week that the action can run, represented as an array of numbers. For example, 1 represents Monday. An empty array is equivalent to specifying all the days of the week.",
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 7),
},
},
"timezone": {
Description: "The ISO time zone for the hours values. Values such as UTC and UTC+1 also work but lack built-in daylight savings time support and are not recommended.",
Type: schema.TypeString,
Required: true,
},
"hours_start": {
Description: "Defines the range of time in a day that the action can run. The start of the time frame in 24-hour notation (hh:mm).",
Type: schema.TypeString,
Required: true,
ValidateFunc: utils.StringIsHours,
},
"hours_end": {
Description: "Defines the range of time in a day that the action can run. The end of the time frame in 24-hour notation (hh:mm).",
Type: schema.TypeString,
Required: true,
ValidateFunc: utils.StringIsHours,
},
},
},
},
},
},
},
},
},
},
Expand Down Expand Up @@ -288,7 +341,7 @@ func getActionsFromResourceData(d *schema.ResourceData, serverVersion *version.V
return []models.AlertingRuleAction{}, diag.Errorf("actions.frequency is only supported for Elasticsearch v8.6 or higher")
}

frequency := models.AlertingRuleActionFrequency{
frequency := models.ActionFrequency{
Summary: d.Get(currentAction + ".frequency.0.summary").(bool),
NotifyWhen: d.Get(currentAction + ".frequency.0.notify_when").(string),
}
Expand All @@ -300,6 +353,34 @@ func getActionsFromResourceData(d *schema.ResourceData, serverVersion *version.V
a.Frequency = &frequency
}

if _, ok := d.GetOk(currentAction + ".alerts_filter"); ok {
if serverVersion.LessThan(alertsFilterMinSupportedVersion) {
return []models.AlertingRuleAction{}, diag.Errorf("actions.alerts_filter is only supported for Elasticsearch v8.9 or higher")
}

resourceDays := d.Get(currentAction + ".alerts_filter.0.timeframe.0.days").([]interface{})
days := []int32{}

for _, a := range resourceDays {
day := int32(a.(int))
days = append(days, day)
}

timeframe := models.AlertsFilterTimeframe{
Days: days,
Timezone: d.Get(currentAction + ".alerts_filter.0.timeframe.0.timezone").(string),
HoursStart: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_start").(string),
HoursEnd: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_end").(string),
}

filter := models.ActionAlertsFilter{
Kql: d.Get(currentAction + ".alerts_filter.0.kql").(string),
Timeframe: timeframe,
}

a.AlertsFilter = &filter
}

actions = append(actions, a)
}
}
Expand Down Expand Up @@ -454,11 +535,31 @@ func resourceRuleRead(ctx context.Context, d *schema.ResourceData, meta interfac
frequency = nil
}

alerts_filter := []interface{}{}

if action.AlertsFilter != nil {
timeframe := []interface{}{}
timeframe = append(timeframe, map[string]interface{}{
"days": action.AlertsFilter.Timeframe.Days,
"timezone": action.AlertsFilter.Timeframe.Timezone,
"hours_start": action.AlertsFilter.Timeframe.HoursStart,
"hours_end": action.AlertsFilter.Timeframe.HoursEnd,
})

alerts_filter = append(alerts_filter, map[string]interface{}{
"kql": action.AlertsFilter.Kql,
"timeframe": timeframe,
})
} else {
alerts_filter = nil
}

actions = append(actions, map[string]interface{}{
"group": action.Group,
"id": action.ID,
"params": string(params),
"frequency": frequency,
"group": action.Group,
"id": action.ID,
"params": string(params),
"frequency": frequency,
"alerts_filter": alerts_filter,
})
}

Expand Down
Loading
Loading