diff --git a/CHANGELOG.md b/CHANGELOG.md index 3388adddf..9d581d9c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ - 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)) +- 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)) ## [0.11.6] - 2024-08-20 diff --git a/docs/resources/kibana_alerting_rule.md b/docs/resources/kibana_alerting_rule.md index 496ad077d..ee7deb0b7 100644 --- a/docs/resources/kibana_alerting_rule.md +++ b/docs/resources/kibana_alerting_rule.md @@ -55,7 +55,6 @@ Could not create API key - Unsupported scheme "ApiKey" for granting API Key - `consumer` (String) The name of the application or feature that owns the rule. - `interval` (String) The check interval, which specifies how frequently the rule conditions are checked. The interval must be specified in seconds, minutes, hours or days. - `name` (String) The name of the rule. While this name does not have to be unique, a distinctive name can help you identify a rule. -- `notify_when` (String) Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values. - `params` (String) The rule parameters, which differ for each rule type. - `rule_type_id` (String) The ID of the rule type that you want to call when the rule is scheduled to run. For more information about the valid values, list the rule types using [Get rule types API](https://www.elastic.co/guide/en/kibana/master/list-rule-types-api.html) or refer to the [Rule types documentation](https://www.elastic.co/guide/en/kibana/master/rule-types.html). @@ -64,10 +63,11 @@ Could not create API key - Unsupported scheme "ApiKey" for granting API Key - `actions` (Block List) An action that runs under defined conditions. (see [below for nested schema](#nestedblock--actions)) - `alert_delay` (Number) A number that indicates how many consecutive runs need to meet the rule conditions for an alert to occur. - `enabled` (Boolean) Indicates if you want to run the rule on an interval basis. +- `notify_when` (String) Required until v8.6.0. Deprecated in v8.13.0. Use the `notify_when` property in the action `frequency` object instead. Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values. - `rule_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID. - `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. - `tags` (List of String) A list of tag names that are applied to the rule. -- `throttle` (String) Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values. +- `throttle` (String) Deprecated in 8.13.0. Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values. ### Read-Only @@ -86,8 +86,21 @@ Required: Optional: +- `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. + +### Nested Schema for `actions.frequency` + +Required: + +- `notify_when` (String) Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values. +- `summary` (Boolean) Indicates whether the action is a summary. + +Optional: + +- `throttle` (String) Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values. + ## Import Import is supported using the following syntax: diff --git a/examples/resources/elasticstack_kibana_alerting_rule/resource_rule_action_frequency.tf b/examples/resources/elasticstack_kibana_alerting_rule/resource_rule_action_frequency.tf new file mode 100644 index 000000000..8bf1a59a5 --- /dev/null +++ b/examples/resources/elasticstack_kibana_alerting_rule/resource_rule_action_frequency.tf @@ -0,0 +1,38 @@ +provider "elasticstack" { + kibana {} +} + +resource "elasticstack_kibana_alerting_rule" "example_with_action_frequency" { + name = "%s" + consumer = "alerts" + params = jsonencode({ + aggType = "avg" + groupBy = "top" + termSize = 10 + timeWindowSize = 10 + timeWindowUnit = "s" + threshold = [10] + thresholdComparator = ">" + index = ["test-index"] + timeField = "@timestamp" + aggField = "version" + termField = "name" + }) + rule_type_id = ".index-threshold" + interval = "1m" + enabled = true + + actions { + # Should be the id of a MS Teams connector + id = elasticstack_kibana_action_connector.index_example.connector_id + group = "threshold met" + params = jsonencode({ + "message" : "foobar" + }) + + frequency { + summary = false + notify_when = "onActionGroupChange" + } + } +} diff --git a/internal/clients/kibana/alerting.go b/internal/clients/kibana/alerting.go index 2228a9278..61db39f35 100644 --- a/internal/clients/kibana/alerting.go +++ b/internal/clients/kibana/alerting.go @@ -20,11 +20,24 @@ func ruleResponseToModel(spaceID string, res *alerting.RuleResponseProperties) * actions := []models.AlertingRuleAction{} for _, action := range res.Actions { - actions = append(actions, models.AlertingRuleAction{ + + a := models.AlertingRuleAction{ Group: action.Group, ID: action.Id, Params: action.Params, - }) + } + + if !alerting.IsNil(action.Frequency) { + frequency := unwrapOptionalField(action.Frequency) + + a.Frequency = &models.AlertingRuleActionFrequency{ + Summary: frequency.Summary, + NotifyWhen: (string)(frequency.NotifyWhen), + Throttle: frequency.Throttle.Get(), + } + } + + actions = append(actions, a) } var alertDelay *float32 @@ -68,11 +81,26 @@ func ruleActionsToActionsInner(ruleActions []models.AlertingRuleAction) []alerti actions := []alerting.ActionsInner{} for index := range ruleActions { action := ruleActions[index] - actions = append(actions, alerting.ActionsInner{ + actionToAppend := alerting.ActionsInner{ Group: action.Group, Id: action.ID, Params: action.Params, - }) + } + + if !alerting.IsNil(action.Frequency) { + frequency := alerting.ActionsInnerFrequency{ + Summary: action.Frequency.Summary, + NotifyWhen: (alerting.NotifyWhen)(action.Frequency.NotifyWhen), + } + + if action.Frequency.Throttle != nil { + frequency.Throttle = *alerting.NewNullableString(action.Frequency.Throttle) + } + + actionToAppend.Frequency = &frequency + } + + actions = append(actions, actionToAppend) } return actions } diff --git a/internal/clients/kibana/alerting_test.go b/internal/clients/kibana/alerting_test.go index 90ce90454..be275d29f 100644 --- a/internal/clients/kibana/alerting_test.go +++ b/internal/clients/kibana/alerting_test.go @@ -71,11 +71,25 @@ func Test_ruleResponseToModel(t *testing.T) { Group: "group-1", Id: "id", Params: map[string]interface{}{}, + Frequency: makePtr(alerting.ActionsInnerFrequency{ + Summary: true, + NotifyWhen: "onThrottleInterval", + Throttle: *alerting.NewNullableString(makePtr("10s")), + }), }, { Group: "group-2", Id: "id", Params: map[string]interface{}{}, + Frequency: makePtr(alerting.ActionsInnerFrequency{ + Summary: true, + NotifyWhen: "onActionGroupChange", + }), + }, + { + Group: "group-3", + Id: "id", + Params: map[string]interface{}{}, }, }, ExecutionStatus: alerting.RuleResponsePropertiesExecutionStatus{ @@ -113,11 +127,25 @@ func Test_ruleResponseToModel(t *testing.T) { Group: "group-1", ID: "id", Params: map[string]interface{}{}, + Frequency: &models.AlertingRuleActionFrequency{ + Summary: true, + NotifyWhen: "onThrottleInterval", + Throttle: makePtr("10s"), + }, }, { Group: "group-2", ID: "id", Params: map[string]interface{}{}, + Frequency: &models.AlertingRuleActionFrequency{ + Summary: true, + NotifyWhen: "onActionGroupChange", + }, + }, + { + Group: "group-3", + ID: "id", + Params: map[string]interface{}{}, }, }, AlertDelay: makePtr(float32(4)), diff --git a/internal/kibana/alerting.go b/internal/kibana/alerting.go index a0db6c607..b8dfdc3d3 100644 --- a/internal/kibana/alerting.go +++ b/internal/kibana/alerting.go @@ -3,6 +3,7 @@ package kibana import ( "context" "encoding/json" + "fmt" "strings" "github.com/elastic/terraform-provider-elasticstack/internal/clients" @@ -17,6 +18,9 @@ import ( 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")) + func ResourceAlertingRule() *schema.Resource { apikeySchema := map[string]*schema.Schema{ "rule_id": { @@ -45,9 +49,9 @@ func ResourceAlertingRule() *schema.Resource { ForceNew: true, }, "notify_when": { - Description: "Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.", + Description: "Required until v8.6.0. Deprecated in v8.13.0. Use the `notify_when` property in the action `frequency` object instead. Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.", Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringInSlice([]string{"onActionGroupChange", "onActiveAlert", "onThrottleInterval"}, false), }, "params": { @@ -93,6 +97,34 @@ func ResourceAlertingRule() *schema.Resource { ValidateFunc: validation.StringIsJSON, DiffSuppressFunc: utils.DiffJsonSuppress, }, + "frequency": { + Description: "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.", + Type: schema.TypeList, + MinItems: 0, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "summary": { + Description: "Indicates whether the action is a summary.", + Type: schema.TypeBool, + Required: true, + }, + "notify_when": { + Description: "Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.", + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"onActionGroupChange", "onActiveAlert", "onThrottleInterval"}, false), + }, + "throttle": { + Description: "Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values.", + Type: schema.TypeString, + Optional: true, + ValidateFunc: utils.StringIsDuration, + }, + }, + }, + }, }, }, }, @@ -110,12 +142,11 @@ func ResourceAlertingRule() *schema.Resource { }, }, "throttle": { - Description: "Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values.", + Description: "Deprecated in 8.13.0. Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values.", Type: schema.TypeString, Optional: true, ValidateFunc: utils.StringIsDuration, }, - "scheduled_task_id": { Description: "ID of the scheduled task that will execute the alert.", Type: schema.TypeString, @@ -190,6 +221,16 @@ func getAlertingRuleFromResourceData(d *schema.ResourceData, serverVersion *vers if v, ok := d.GetOk("notify_when"); ok { rule.NotifyWhen = utils.Pointer(v.(string)) + } else { + if serverVersion.LessThan(frequencyMinSupportedVersion) { + return models.AlertingRule{}, diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "notify_when is required until v8.6", + Detail: "notify_when is required until v8.6", + }, + } + } } if v, ok := d.GetOk("alert_delay"); ok { @@ -201,13 +242,12 @@ func getAlertingRuleFromResourceData(d *schema.ResourceData, serverVersion *vers Detail: "alert_delay is only supported for Elasticsearch v8.13 or higher", }, } - } rule.AlertDelay = utils.Pointer(float32(v.(float64))) } - actions, diags := getActionsFromResourceData(d) + actions, diags := getActionsFromResourceData(d, serverVersion) if diags.HasError() { return models.AlertingRule{}, diags } @@ -222,11 +262,11 @@ func getAlertingRuleFromResourceData(d *schema.ResourceData, serverVersion *vers return rule, diags } -func getActionsFromResourceData(d *schema.ResourceData) ([]models.AlertingRuleAction, diag.Diagnostics) { +func getActionsFromResourceData(d *schema.ResourceData, serverVersion *version.Version) ([]models.AlertingRuleAction, diag.Diagnostics) { actions := []models.AlertingRuleAction{} if v, ok := d.GetOk("actions"); ok { resourceActions := v.([]interface{}) - for _, a := range resourceActions { + for i, a := range resourceActions { action := a.(map[string]interface{}) paramsStr := action["params"].(string) var params map[string]interface{} @@ -235,11 +275,32 @@ func getActionsFromResourceData(d *schema.ResourceData) ([]models.AlertingRuleAc return []models.AlertingRuleAction{}, diag.FromErr(err) } - actions = append(actions, models.AlertingRuleAction{ + a := models.AlertingRuleAction{ Group: action["group"].(string), ID: action["id"].(string), Params: params, - }) + } + + currentAction := fmt.Sprintf("actions.%d", i) + + if _, ok := d.GetOk(currentAction + ".frequency"); ok { + if serverVersion.LessThan(frequencyMinSupportedVersion) { + return []models.AlertingRuleAction{}, diag.Errorf("actions.frequency is only supported for Elasticsearch v8.6 or higher") + } + + frequency := models.AlertingRuleActionFrequency{ + Summary: d.Get(currentAction + ".frequency.0.summary").(bool), + NotifyWhen: d.Get(currentAction + ".frequency.0.notify_when").(string), + } + + if throttle := getOrNilString(currentAction+".frequency.0.throttle", d); throttle != nil && *throttle != "" { + frequency.Throttle = throttle + } + + a.Frequency = &frequency + } + + actions = append(actions, a) } } @@ -380,12 +441,27 @@ func resourceRuleRead(ctx context.Context, d *schema.ResourceData, meta interfac if err != nil { return diag.FromErr(err) } + + frequency := []interface{}{} + + if action.Frequency != nil { + frequency = append(frequency, map[string]interface{}{ + "summary": action.Frequency.Summary, + "notify_when": action.Frequency.NotifyWhen, + "throttle": action.Frequency.Throttle, + }) + } else { + frequency = nil + } + actions = append(actions, map[string]interface{}{ - "group": action.Group, - "id": action.ID, - "params": string(params), + "group": action.Group, + "id": action.ID, + "params": string(params), + "frequency": frequency, }) } + if err := d.Set("actions", actions); err != nil { return diag.FromErr(err) } diff --git a/internal/models/alert_rule.go b/internal/models/alert_rule.go index 1bbae824a..31378c8c8 100644 --- a/internal/models/alert_rule.go +++ b/internal/models/alert_rule.go @@ -1,6 +1,8 @@ package models -import "time" +import ( + "time" +) type AlertingRule struct { RuleID string @@ -26,12 +28,19 @@ type AlertingRuleSchedule struct { } type AlertingRuleAction struct { - Group string - ID string - Params map[string]interface{} + Group string + ID string + Params map[string]interface{} + Frequency *AlertingRuleActionFrequency } type AlertingRuleExecutionStatus struct { LastExecutionDate *time.Time Status *string } + +type AlertingRuleActionFrequency struct { + Summary bool + NotifyWhen string + Throttle *string +}