diff --git a/docs/resources/alert_rule.md b/docs/resources/alert_rule.md index af8e6453..61eae5e2 100644 --- a/docs/resources/alert_rule.md +++ b/docs/resources/alert_rule.md @@ -21,12 +21,12 @@ resource "lacework_alert_channel_slack" "ops_critical" { } resource "lacework_alert_rule" "example" { - name = "My Alert Rule" - description = "This is an example alert rule" - alert_channels = [lacework_alert_channel_slack.ops_critical.id] - severities = ["Critical"] - event_categories = ["Compliance"] - alert_categories = ["Policy"] + name = "My Alert Rule" + description = "This is an example alert rule" + alert_channels = [lacework_alert_channel_slack.ops_critical.id] + severities = ["Critical"] + alert_subcategories = ["Compliance"] + alert_categories = ["Policy"] } ``` @@ -45,12 +45,12 @@ resource "lacework_resource_group_gcp" "all_gcp_projects" { } resource "lacework_alert_rule" "example" { - name = "My Alert Rule" - description = "This is an example alert rule" - alert_channels = [lacework_alert_channel_slack.ops_critical.id] - severities = ["Critical"] - event_categories = ["Compliance"] - resource_groups = [lacework_resource_group_gcp.all_gcp_projects.id] + name = "My Alert Rule" + description = "This is an example alert rule" + alert_channels = [lacework_alert_channel_slack.ops_critical.id] + severities = ["Critical"] + alert_subcategories = ["Compliance"] + resource_groups = [lacework_resource_group_gcp.all_gcp_projects.id] } ``` @@ -63,12 +63,17 @@ The following arguments are supported: * `severities` - (Required) The list of the severities that the rule will apply. Valid severities include: `Critical`, `High`, `Medium`, `Low` and `Info`. * `description` - (Optional) The description of the alert rule. -* `event_categories` - (Optional) The list of event categories the rule will apply to. Valid categories include: +* `alert_subcategories` - (Optional) The list of alert subcategories the rule will apply to. Valid categories include: `Compliance`, `App`, `Cloud`, `File`, `Machine`, `User`, `Platform`, `K8sActivity`, `Registry` `SystemCall`. * `alert_categories` - (Optional) The alert categories that will use this rule for alert routing. Valid categories include: `Anomaly`, `Policy`, `Composite`. * `resource_groups` - (Optional) The list of resource groups the rule will apply to. * `enabled` - (Optional) The state of the external integration. Defaults to `true`. +* `event_categories` - (Optional, **Deprecated**) The list of event categories the rule will apply to. Valid categories include: + `Compliance`, `App`, `Cloud`, `File`, `Machine`, `User`, `Platform`, `K8sActivity`, `Registry` `SystemCall`. +This attribute is deprecated use `alert_subcategories` instead. + + ## Import diff --git a/examples/resource_lacework_alert_rule/current/main.tf b/examples/resource_lacework_alert_rule/current/main.tf new file mode 100644 index 00000000..4d56474d --- /dev/null +++ b/examples/resource_lacework_alert_rule/current/main.tf @@ -0,0 +1,85 @@ +terraform { + required_providers { + lacework = { + source = "lacework/lacework" + } + } +} + +resource "lacework_alert_rule" "example" { + name = var.name + description = var.description + alert_channels = var.channels + severities = var.severities + alert_subcategories = var.alert_subcategories + alert_categories = var.alert_categories + resource_groups = [lacework_resource_group_aws.example.id] +} + +resource "lacework_resource_group_aws" "example" { + name = var.resource_group_name + accounts = ["*"] +} + +variable "resource_group_name" { + type = string + default = "Users for Alert Rules Testing example" +} + +variable "name" { + type = string + default = "Alert Rule" +} + +variable "description" { + type = string + default = "Alert Rule created by Terraform" +} + +variable "channels" { + type = list(string) + default = ["TECHALLY_013F08F1B3FA97E7D54463DECAEEACF9AEA3AEACF863F76"] +} + +variable "severities" { + type = list(string) + default = ["High", "Medium"] +} + +variable "alert_subcategories" { + type = list(string) + default = ["Compliance", "Platform", "User", "Cloud"] +} + +variable "alert_categories" { + type = list(string) + default = ["Policy"] +} + +output "name" { + value = lacework_alert_rule.example.name +} + +output "description" { + value = lacework_alert_rule.example.description +} + +output "channels" { + value = lacework_alert_rule.example.alert_channels +} + +output "severities" { + value = lacework_alert_rule.example.severities +} + +output "alert_subcategories" { + value = lacework_alert_rule.example.alert_subcategories +} + +output "alert_categories" { + value = lacework_alert_rule.example.alert_categories +} + +output "resource_group_id" { + value = lacework_resource_group_aws.example.id +} diff --git a/examples/resource_lacework_alert_rule/main.tf b/examples/resource_lacework_alert_rule/deprecated/main.tf similarity index 100% rename from examples/resource_lacework_alert_rule/main.tf rename to examples/resource_lacework_alert_rule/deprecated/main.tf diff --git a/integration/resource_lacework_alert_rule_test.go b/integration/resource_lacework_alert_rule_test.go index 4454a942..b12ae823 100644 --- a/integration/resource_lacework_alert_rule_test.go +++ b/integration/resource_lacework_alert_rule_test.go @@ -19,14 +19,14 @@ import ( func TestAlertRuleCreate(t *testing.T) { name := fmt.Sprintf("Alert Rule - %s", time.Now()) terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_alert_rule", + TerraformDir: "../examples/resource_lacework_alert_rule/current", EnvVars: tokenEnvVar, Vars: map[string]interface{}{ "name": name, "description": "Alert Rule created by Terraform", "channels": []string{"TECHALLY_013F08F1B3FA97E7D54463DECAEEACF9AEA3AEACF863F76"}, "severities": []string{"Critical"}, - "event_categories": []string{"Compliance"}, + "alert_subcategories": []string{"Compliance"}, "resource_group_name": fmt.Sprintf("Used for Alert Rule Test - %s", time.Now()), }, }) @@ -41,7 +41,7 @@ func TestAlertRuleCreate(t *testing.T) { actualDescription := terraform.Output(t, terraformOptions, "description") actualChannels := terraform.Output(t, terraformOptions, "channels") actualSeverities := terraform.Output(t, terraformOptions, "severities") - actualEventCategories := terraform.Output(t, terraformOptions, "event_categories") + actualEventCategories := terraform.Output(t, terraformOptions, "alert_subcategories") actualResourceGroupID := terraform.Output(t, terraformOptions, "resource_group_id") assert.Equal(t, "Alert Rule created by Terraform", createProps.Data.Filter.Description) @@ -63,7 +63,7 @@ func TestAlertRuleCreate(t *testing.T) { "channels": []string{"TECHALLY_01BA9DCAF34B654254D6BF92E5C24023951C3F812B07527", "TECHALLY_013F08F1B3FA97E7D54463DECAEEACF9AEA3AEACF863F76"}, "severities": []string{"High", "Medium"}, - "event_categories": []string{"Compliance", "User", "Platform"}, + "alert_subcategories": []string{"Compliance", "User", "Platform"}, "resource_group_name": fmt.Sprintf("Used for Alert Rule Test - %s", time.Now()), } @@ -72,7 +72,7 @@ func TestAlertRuleCreate(t *testing.T) { actualDescription = terraform.Output(t, terraformOptions, "description") actualChannels = terraform.Output(t, terraformOptions, "channels") actualSeverities = terraform.Output(t, terraformOptions, "severities") - actualEventCategories = terraform.Output(t, terraformOptions, "event_categories") + actualEventCategories = terraform.Output(t, terraformOptions, "alert_subcategories") actualResourceGroupID = terraform.Output(t, terraformOptions, "resource_group_id") assert.Equal(t, "Updated Alert Rule created by Terraform", updateProps.Data.Filter.Description) @@ -91,7 +91,7 @@ func TestAlertRuleCreate(t *testing.T) { func TestAlertRuleSeverities(t *testing.T) { name := fmt.Sprintf("Alert Rule - %s", time.Now()) terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_alert_rule", + TerraformDir: "../examples/resource_lacework_alert_rule/current", EnvVars: tokenEnvVar, Vars: map[string]interface{}{ "name": name, @@ -114,7 +114,7 @@ func TestAlertRuleSeverities(t *testing.T) { assert.Equal(t, "[Critical High Medium Low]", actualSeverities) invalidOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_alert_rule", + TerraformDir: "../examples/resource_lacework_alert_rule/current", Vars: map[string]interface{}{ "name": name, "severities": []string{"INVALID"}, @@ -134,7 +134,55 @@ func TestAlertRuleSeverities(t *testing.T) { func TestAlertRuleCategories(t *testing.T) { name := fmt.Sprintf("Alert Rule - %s", time.Now()) terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_alert_rule", + TerraformDir: "../examples/resource_lacework_alert_rule/current", + EnvVars: tokenEnvVar, + Vars: map[string]interface{}{ + "name": name, + "alert_subcategories": []string{"Compliance", "App", "Cloud", "File", "Machine", + "User", "Platform", "K8sActivity", "Registry", "SystemCall"}, + "alert_categories": []string{"Policy"}, + "resource_group_name": fmt.Sprintf("Used for Alert Rule Test - %s", time.Now()), + }, + }) + defer terraform.Destroy(t, terraformOptions) + + terraformOptions.TimeBetweenRetries = 2 * time.Second + create := terraform.InitAndApplyAndIdempotent(t, terraformOptions) + createProps := GetAlertRuleProps(create) + + actualCategories := terraform.Output(t, terraformOptions, "alert_subcategories") + actualAlertCategories := terraform.Output(t, terraformOptions, "alert_categories") + + assert.ElementsMatch(t, []string{"Compliance", "App", "Cloud", "File", "Machine", + "User", "Platform", "K8sActivity", "Registry", "SystemCall"}, createProps.Data.Filter.EventCategories) + assert.ElementsMatch(t, []string{"Policy"}, createProps.Data.Filter.AlertCategories) + + assert.Equal(t, "[App Cloud Compliance File K8sActivity Machine Platform Registry SystemCall User]", + actualCategories) + assert.Equal(t, "[Policy]", actualAlertCategories) + + invalidOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "../examples/resource_lacework_alert_rule/current", + Vars: map[string]interface{}{ + "name": name, + "alert_subcategories": []string{"INVALID"}, + "resource_group_name": fmt.Sprintf("Used for Alert Rule Test - %s", time.Now()), + }, + }) + + _, err := terraform.ApplyE(t, invalidOptions) + if assert.Error(t, err) { + assert.Contains(t, + err.Error(), + "expected alert_subcategories.0 to be one of [Compliance App Cloud File Machine User Platform K8sActivity Registry SystemCall]", + ) + } +} + +func TestAlertRuleDeprecatedEventCategories(t *testing.T) { + name := fmt.Sprintf("Alert Rule - %s", time.Now()) + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "../examples/resource_lacework_alert_rule/deprecated", EnvVars: tokenEnvVar, Vars: map[string]interface{}{ "name": name, @@ -162,7 +210,7 @@ func TestAlertRuleCategories(t *testing.T) { assert.Equal(t, "[Policy]", actualAlertCategories) invalidOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_alert_rule", + TerraformDir: "../examples/resource_lacework_alert_rule/deprecated", Vars: map[string]interface{}{ "name": name, "event_categories": []string{"INVALID"}, diff --git a/lacework/resource_lacework_alert_rule.go b/lacework/resource_lacework_alert_rule.go index 6a08c9f9..2baad3e0 100644 --- a/lacework/resource_lacework_alert_rule.go +++ b/lacework/resource_lacework_alert_rule.go @@ -102,8 +102,9 @@ func resourceLaceworkAlertRule() *schema.Resource { }, }, "event_categories": { - Type: schema.TypeSet, - Optional: true, + Type: schema.TypeSet, + Optional: true, + Deprecated: "This attribute is deprecated and has been replaced by `alert_subcategories`", Description: fmt.Sprintf("List of event categories for the alert rule. Valid categories are: %s", strings.Join(api.AlertRuleSubCategories, ", ")), Elem: &schema.Schema{ @@ -114,6 +115,20 @@ func resourceLaceworkAlertRule() *schema.Resource { ValidateFunc: validation.StringInSlice(api.AlertRuleSubCategories, false), }, }, + "alert_subcategories": { + Type: schema.TypeSet, + Optional: true, + ConflictsWith: []string{"event_categories"}, + Description: fmt.Sprintf("List of alert subcategories for the alert rule. Valid categories are: %s", + strings.Join(api.AlertRuleSubCategories, ", ")), + Elem: &schema.Schema{ + Type: schema.TypeString, + StateFunc: func(val interface{}) string { + return strings.TrimSpace(val.(string)) + }, + ValidateFunc: validation.StringInSlice(api.AlertRuleSubCategories, false), + }, + }, "guid": { Type: schema.TypeString, Computed: true, @@ -140,10 +155,16 @@ func resourceLaceworkAlertRuleCreate(d *schema.ResourceData, meta interface{}) e alertChannels = d.Get("alert_channels").(*schema.Set).List() } + var alertSubcategories []interface{} + if _, ok := d.GetOk("alert_subcategories"); ok { + alertSubcategories = d.Get("alert_subcategories").(*schema.Set).List() + } else if _, ok := d.GetOk("event_categories"); ok { + alertSubcategories = d.Get("event_categories").(*schema.Set).List() + } + var ( lacework = meta.(*api.Client) resourceGroups = d.Get("resource_groups").(*schema.Set).List() - eventCategories = d.Get("event_categories").(*schema.Set).List() alertCategories = d.Get("alert_categories").(*schema.Set).List() severities = api.NewAlertRuleSeverities(castAttributeToStringSlice(d, "severities")) alertRule = api.NewAlertRule(d.Get("name").(string), @@ -151,7 +172,7 @@ func resourceLaceworkAlertRuleCreate(d *schema.ResourceData, meta interface{}) e Description: d.Get("description").(string), Channels: castStringSlice(alertChannels), Severities: severities, - EventCategories: castStringSlice(eventCategories), + EventCategories: castStringSlice(alertSubcategories), AlertCategories: castStringSlice(alertCategories), ResourceGroups: castStringSlice(resourceGroups), }, @@ -202,7 +223,11 @@ func resourceLaceworkAlertRuleRead(d *schema.ResourceData, meta interface{}) err d.Set("type_name", response.Data.Type) d.Set("severities", api.NewAlertRuleSeveritiesFromIntSlice(response.Data.Filter.Severity).ToStringSlice()) d.Set("resource_groups", response.Data.Filter.ResourceGroups) - d.Set("event_categories", response.Data.Filter.EventCategories) + if _, ok := d.GetOk("alert_subcategories"); ok { + d.Set("alert_subcategories", response.Data.Filter.EventCategories) + } else if _, ok := d.GetOk("event_categories"); ok { + d.Set("event_categories", response.Data.Filter.EventCategories) + } d.Set("alert_categories", response.Data.Filter.AlertCategories) d.Set("alert_channels", response.Data.Channels) @@ -216,10 +241,16 @@ func resourceLaceworkAlertRuleUpdate(d *schema.ResourceData, meta interface{}) e alertChannels = d.Get("alert_channels").(*schema.Set).List() } + var alertSubcategories []interface{} + if _, ok := d.GetOk("alert_subcategories"); ok { + alertSubcategories = d.Get("alert_subcategories").(*schema.Set).List() + } else if _, ok := d.GetOk("event_categories"); ok { + alertSubcategories = d.Get("event_categories").(*schema.Set).List() + } + var ( lacework = meta.(*api.Client) resourceGroups = d.Get("resource_groups").(*schema.Set).List() - eventCategories = d.Get("event_categories").(*schema.Set).List() alertCategories = d.Get("alert_categories").(*schema.Set).List() severities = api.NewAlertRuleSeverities(castAttributeToStringSlice(d, "severities")) alertRule = api.NewAlertRule(d.Get("name").(string), @@ -227,7 +258,7 @@ func resourceLaceworkAlertRuleUpdate(d *schema.ResourceData, meta interface{}) e Description: d.Get("description").(string), Channels: castStringSlice(alertChannels), Severities: severities, - EventCategories: castStringSlice(eventCategories), + EventCategories: castStringSlice(alertSubcategories), AlertCategories: castStringSlice(alertCategories), ResourceGroups: castStringSlice(resourceGroups), },