From a963535df25571ad4b80d3ae1be88005b8b1a2d5 Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Fri, 21 Nov 2025 06:34:20 +0100 Subject: [PATCH 1/3] feat(cockpit): add preconfigured_alert_ids field and deprecate enable_managed_alerts --- .../cockpit_preconfigured_alert.md | 100 ++++++++++ .../migration_guide_cockpit_alert_manager.md | 166 +++++++++++++++++ docs/resources/cockpit_alert_manager.md | 65 ++++++- internal/services/cockpit/alert_manager.go | 157 ++++++++++++---- .../services/cockpit/alert_manager_test.go | 171 +++++++++++++++-- .../preconfigured_alert_data_source.go | 175 ++++++++++++++++++ .../preconfigured_alert_data_source_test.go | 74 ++++++++ provider/sdkv2.go | 1 + .../cockpit_preconfigured_alert.md.tmpl | 100 ++++++++++ .../migration_guide_cockpit_alert_manager.md | 166 +++++++++++++++++ .../resources/cockpit_alert_manager.md.tmpl | 65 ++++++- 11 files changed, 1176 insertions(+), 64 deletions(-) create mode 100644 docs/data-sources/cockpit_preconfigured_alert.md create mode 100644 docs/guides/migration_guide_cockpit_alert_manager.md create mode 100644 internal/services/cockpit/preconfigured_alert_data_source.go create mode 100644 internal/services/cockpit/preconfigured_alert_data_source_test.go create mode 100644 templates/data-sources/cockpit_preconfigured_alert.md.tmpl create mode 100644 templates/guides/migration_guide_cockpit_alert_manager.md diff --git a/docs/data-sources/cockpit_preconfigured_alert.md b/docs/data-sources/cockpit_preconfigured_alert.md new file mode 100644 index 0000000000..c3f84efa94 --- /dev/null +++ b/docs/data-sources/cockpit_preconfigured_alert.md @@ -0,0 +1,100 @@ +--- +subcategory: "Cockpit" +page_title: "Scaleway: scaleway_cockpit_preconfigured_alert" +--- + +# Data Source: scaleway_cockpit_preconfigured_alert + +Gets information about preconfigured alert rules available in Scaleway Cockpit. + +Preconfigured alerts are ready-to-use alert rules that monitor common metrics for Scaleway services. +You can enable these alerts in your Alert Manager using the `scaleway_cockpit_alert_manager` resource. + +For more information, refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/) and [API documentation](https://www.scaleway.com/en/developers/api/cockpit/regional-api). + +## Example Usage + +### Basic usage + +```terraform +data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.main.alerts +} +``` + +### Filter by status + +```terraform +data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_account_project.project.id + rule_status = "enabled" +} + +data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_account_project.project.id + rule_status = "disabled" +} +``` + +### Use with Alert Manager + +```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific alerts by their preconfigured_rule_id + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" && alert.rule_status == "disabled" + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Argument Reference + +- `project_id` - (Optional) The ID of the project the alerts are associated with. If not provided, the default project configured in the provider is used. +- `region` - (Optional, defaults to provider region) The region in which the alerts exist. +- `data_source_id` - (Optional) Filter alerts by data source ID. +- `rule_status` - (Optional) Filter alerts by rule status. Valid values are `enabled` or `disabled`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the resource (project ID with region). +- `alerts` - List of preconfigured alerts. Each alert contains: + - `name` - Name of the alert rule. + - `rule` - PromQL expression defining the alert condition. + - `duration` - Duration for which the condition must be true before the alert fires (e.g., "5m"). + - `rule_status` - Status of the alert rule (`enabled`, `disabled`, `enabling`, `disabling`). + - `state` - Current state of the alert (`inactive`, `pending`, `firing`). + - `annotations` - Map of annotations attached to the alert. + - `preconfigured_rule_id` - Unique identifier of the preconfigured rule. Use this ID in `scaleway_cockpit_alert_manager` resource. + - `display_name` - Human-readable name of the alert. + - `display_description` - Human-readable description of the alert. + - `product_name` - Scaleway product associated with the alert (e.g., "instance", "rdb", "kubernetes"). + - `product_family` - Family of the product (e.g., "compute", "storage", "network"). + - `data_source_id` - ID of the data source containing the alert rule. + + diff --git a/docs/guides/migration_guide_cockpit_alert_manager.md b/docs/guides/migration_guide_cockpit_alert_manager.md new file mode 100644 index 0000000000..396fe44c60 --- /dev/null +++ b/docs/guides/migration_guide_cockpit_alert_manager.md @@ -0,0 +1,166 @@ +--- +page_title: "Cockpit Alert Manager Migration Guide" +--- + +# Cockpit Alert Manager Migration Guide + +This guide explains how to migrate from the deprecated `enable_managed_alerts` field to the new `preconfigured_alert_ids` field in the `scaleway_cockpit_alert_manager` resource. + +## Background + +The `enable_managed_alerts` field is being deprecated in favor of a more flexible approach using `preconfigured_alert_ids`. This change provides: + +- **Granular control**: Select specific alerts instead of enabling all managed alerts +- **Better visibility**: Explicitly declare which alerts are enabled in your Terraform configuration +- **Improved state management**: Terraform accurately tracks which alerts are active + +## Migration Steps + +### Before Migration (Deprecated) + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alerts@example.com" + } +} +``` + +### After Migration (Recommended) + +#### Step 1: List Available Preconfigured Alerts + +Use the data source to discover available alerts: + +```terraform +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.all.alerts +} +``` + +Run `terraform apply` and review the output to see available alerts. + +#### Step 2: Select Specific Alerts + +Choose the alerts you want to enable: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable specific alerts by product/family + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if contains(["PostgreSQL", "MySQL"], alert.product_name) + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +Or use specific alert IDs: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7", # MySQL Too Many Connections + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Filtering Alerts + +### By Product Name + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "Kubernetes" +] +``` + +### By Product Family + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Managed Databases" +] +``` + +### Multiple Criteria + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Load Balancer" && alert.product_name == "LB" +] +``` + +## Important Notes + +### Behavioral Changes + +- **No automatic alerts**: Unlike `enable_managed_alerts = true`, the API will not automatically enable additional alerts +- **Explicit configuration**: You must explicitly list all alerts you want to enable +- **State accuracy**: Terraform state will only track alerts you've configured + +### Compatibility + +- The deprecated `enable_managed_alerts` field will be removed in a future major version +- Both fields can coexist during migration, but `preconfigured_alert_ids` takes precedence +- If neither field is specified, no preconfigured alerts will be enabled + +## Troubleshooting + +### "Insufficient permissions" Error + +If you see permission errors when using the `scaleway_cockpit_preconfigured_alert` data source, ensure your IAM policy includes: + +```json +{ + "permission_sets": [ + { + "name": "CockpitManager", + "permissions": [ + "read:cockpit" + ] + } + ] +} +``` + +### Unexpected State Changes + +If Terraform shows unexpected changes to `preconfigured_alert_ids`: + +1. Verify the alert IDs still exist by querying the data source +2. Check that alerts are in `enabled` or `enabling` state +3. Ensure no manual changes were made outside Terraform + +## Additional Resources + +- [Cockpit Alert Manager Resource Documentation](../resources/cockpit_alert_manager.md) +- [Cockpit Preconfigured Alert Data Source Documentation](../data-sources/cockpit_preconfigured_alert.md) +- [Scaleway Cockpit Documentation](https://www.scaleway.com/en/docs/observability/cockpit/) + diff --git a/docs/resources/cockpit_alert_manager.md b/docs/resources/cockpit_alert_manager.md index 2320635371..c32aa501f0 100644 --- a/docs/resources/cockpit_alert_manager.md +++ b/docs/resources/cockpit_alert_manager.md @@ -12,23 +12,48 @@ Refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/obse ## Example Usage -### Enable the alert manager and configure managed alerts +### Enable preconfigured alerts (Recommended) -The following commands allow you to: - -- enable the alert manager in a Project named `tf_test_project` -- enable [managed alerts](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#managed-alerts) -- set up [contact points](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#contact-points) to receive alert notifications +Use preconfigured alerts to monitor your Scaleway resources with ready-to-use alert rules: ```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific preconfigured alerts + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" + ] + contact_points { + email = "alerts@example.com" + } +} +``` + +### Enable the alert manager with contact points + +```terraform resource "scaleway_account_project" "project" { name = "tf_test_project" } resource "scaleway_cockpit_alert_manager" "alert_manager" { - project_id = scaleway_account_project.project.id - enable_managed_alerts = true + project_id = scaleway_account_project.project.id contact_points { email = "alert1@example.com" @@ -40,13 +65,33 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { } ``` +### Legacy: Enable managed alerts (Deprecated) + +~> **Deprecated:** The `enable_managed_alerts` field is deprecated. Use `preconfigured_alert_ids` instead. + +```terraform +resource "scaleway_account_project" "project" { + name = "tf_test_project" +} + +resource "scaleway_cockpit_alert_manager" "alert_manager" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alert@example.com" + } +} +``` + ## Argument Reference This section lists the arguments that are supported: -- `enable_managed_alerts` - (Optional, Boolean) Specifies whether the alert manager should be enabled. Defaults to true. -- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key email. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. +- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. - `region` - (Defaults to the region specified in the [provider configuration](../index.md#arguments-reference)) The [region](../guides/regions_and_zones.md#regions) where the [alert manager](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#alert-manager) should be enabled. diff --git a/internal/services/cockpit/alert_manager.go b/internal/services/cockpit/alert_manager.go index 2bc4d7f93c..7def2634b0 100644 --- a/internal/services/cockpit/alert_manager.go +++ b/internal/services/cockpit/alert_manager.go @@ -29,10 +29,16 @@ func ResourceCockpitAlertManager() *schema.Resource { "enable_managed_alerts": { Type: schema.TypeBool, Optional: true, - Default: true, - Description: "Enable or disable the alert manager", + Computed: true, + Deprecated: "Use 'preconfigured_alert_ids' instead. This field will be removed in a future version.", + Description: "Enable or disable the alert manager (deprecated)", + }, + "preconfigured_alert_ids": { + Type: schema.TypeSet, + Optional: true, + Description: "List of preconfigured alert rule IDs to enable. Use the scaleway_cockpit_preconfigured_alert data source to list available alerts.", + Elem: &schema.Schema{Type: schema.TypeString}, }, - "contact_points": { Type: schema.TypeList, Optional: true, @@ -66,23 +72,30 @@ func ResourceCockpitAlertManagerCreate(ctx context.Context, d *schema.ResourceDa projectID := d.Get("project_id").(string) contactPoints := d.Get("contact_points").([]any) - EnableManagedAlerts := d.Get("enable_managed_alerts").(bool) _, err = api.EnableAlertManager(&cockpit.RegionalAPIEnableAlertManagerRequest{ Region: region, ProjectID: projectID, - }) + }, scw.WithContext(ctx)) if err != nil { return diag.FromErr(err) } - if EnableManagedAlerts { - _, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{ - Region: region, - ProjectID: projectID, - }) - if err != nil { - return diag.FromErr(err) + // Handle preconfigured alerts + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + alertIDs := expandStringSet(v.(*schema.Set)) + if len(alertIDs) > 0 { + _, err = api.EnableAlertRules(&cockpit.RegionalAPIEnableAlertRulesRequest{ + Region: region, + ProjectID: projectID, + RuleIDs: alertIDs, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available + // For now, we continue without waiting as the Read function handles enabling/enabled states } } @@ -138,11 +151,56 @@ func ResourceCockpitAlertManagerRead(ctx context.Context, d *schema.ResourceData return diag.FromErr(err) } - _ = d.Set("enable_managed_alerts", alertManager.ManagedAlertsEnabled) - _ = d.Set("region", alertManager.Region) + // Note: We don't set "enable_managed_alerts" here because it's automatically + // managed by the API when preconfigured alerts are enabled/disabled. + // Setting it would cause perpetual drift. + _ = d.Set("region", string(alertManager.Region)) _ = d.Set("alert_manager_url", alertManager.AlertManagerURL) _ = d.Set("project_id", projectID) + // Get enabled preconfigured alerts + // Only track the alerts that were explicitly requested by the user in the configuration. + // The API may enable additional alerts automatically, but we ignore those to prevent perpetual drift. + // We set the field based on what was requested in the config, not what the API returns. + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + requestedIDs := expandStringSet(v.(*schema.Set)) + + // List all preconfigured alerts to verify their status + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + }, scw.WithContext(ctx), scw.WithAllPages()) + if err != nil { + return diag.FromErr(err) + } + + // Build a map of alert statuses + alertStatusMap := make(map[string]cockpit.AlertStatus) + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { + alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus + } + } + + // Keep the requested IDs that are enabled/enabling + // This ensures we only track what the user explicitly configured + var enabledRequestedIDs []string + for _, requestedID := range requestedIDs { + if status, found := alertStatusMap[requestedID]; found { + // Include if enabled or enabling + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + enabledRequestedIDs = append(enabledRequestedIDs, requestedID) + } + } + } + + _ = d.Set("preconfigured_alert_ids", enabledRequestedIDs) + } else { + // No alerts requested, set empty list + _ = d.Set("preconfigured_alert_ids", []string{}) + } + contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region, ProjectID: projectID, @@ -179,22 +237,39 @@ func ResourceCockpitAlertManagerUpdate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - if d.HasChange("enable_managed_alerts") { - enable := d.Get("enable_managed_alerts").(bool) - if enable { - _, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{ + if d.HasChange("preconfigured_alert_ids") { + oldIDs, newIDs := d.GetChange("preconfigured_alert_ids") + oldSet := oldIDs.(*schema.Set) + newSet := newIDs.(*schema.Set) + + // IDs to disable: in old but not in new + toDisable := expandStringSet(oldSet.Difference(newSet)) + if len(toDisable) > 0 { + _, err = api.DisableAlertRules(&cockpit.RegionalAPIDisableAlertRulesRequest{ Region: region, ProjectID: projectID, - }) - } else { - _, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{ + RuleIDs: toDisable, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + // Note: Waiting for alerts to be disabled will be handled by SDK waiters when available + } + + // IDs to enable: in new but not in old + toEnable := expandStringSet(newSet.Difference(oldSet)) + if len(toEnable) > 0 { + _, err = api.EnableAlertRules(&cockpit.RegionalAPIEnableAlertRulesRequest{ Region: region, ProjectID: projectID, + RuleIDs: toEnable, }, scw.WithContext(ctx)) - } + if err != nil { + return diag.FromErr(err) + } - if err != nil { - return diag.FromErr(err) + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available } } @@ -263,6 +338,21 @@ func ResourceCockpitAlertManagerDelete(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } + // Disable all preconfigured alerts if any are enabled + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + alertIDs := expandStringSet(v.(*schema.Set)) + if len(alertIDs) > 0 { + _, err = api.DisableAlertRules(&cockpit.RegionalAPIDisableAlertRulesRequest{ + Region: region, + ProjectID: projectID, + RuleIDs: alertIDs, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + } + } + contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region, ProjectID: projectID, @@ -284,18 +374,10 @@ func ResourceCockpitAlertManagerDelete(ctx context.Context, d *schema.ResourceDa } } - _, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{ - Region: region, - ProjectID: projectID, - }, scw.WithContext(ctx)) - if err != nil { - return diag.FromErr(err) - } - _, err = api.DisableAlertManager(&cockpit.RegionalAPIDisableAlertManagerRequest{ Region: region, ProjectID: projectID, - }) + }, scw.WithContext(ctx)) if err != nil { return diag.FromErr(err) } @@ -321,3 +403,12 @@ func ResourceCockpitAlertManagerParseID(resourceID string) (region scw.Region, p return scw.Region(parts[0]), parts[1], nil } + +func expandStringSet(set *schema.Set) []string { + result := make([]string, set.Len()) + for i, v := range set.List() { + result[i] = v.(string) + } + + return result +} diff --git a/internal/services/cockpit/alert_manager_test.go b/internal/services/cockpit/alert_manager_test.go index 53043d1098..755763f06b 100644 --- a/internal/services/cockpit/alert_manager_test.go +++ b/internal/services/cockpit/alert_manager_test.go @@ -30,7 +30,6 @@ func TestAccCockpitAlertManager_CreateWithSingleContact(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "initial@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), @@ -42,7 +41,6 @@ func TestAccCockpitAlertManager_CreateWithSingleContact(t *testing.T) { {"email": "updated@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "updated@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), @@ -68,7 +66,6 @@ func TestAccCockpitAlertManager_CreateWithMultipleContacts(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "initial1@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "initial2@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -82,7 +79,6 @@ func TestAccCockpitAlertManager_CreateWithMultipleContacts(t *testing.T) { {"email": "updated2@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "updated1@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "updated2@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -109,7 +105,6 @@ func TestAccCockpitAlertManager_UpdateSingleContact(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "notupdated@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "initial1@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -123,7 +118,6 @@ func TestAccCockpitAlertManager_UpdateSingleContact(t *testing.T) { {"email": "updated1@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "notupdated@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "updated1@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -148,7 +142,6 @@ func TestAccCockpitAlertManager_EnableDisable(t *testing.T) { { Config: testAccCockpitAlertManagerEnableConfig(true), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), testAccCheckAlertManagerEnabled(tt, "scaleway_cockpit_alert_manager.alert_manager", true), @@ -181,7 +174,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource "scaleway_cockpit_alert_manager" "main" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true contact_points { email = "test@example.com" @@ -192,7 +184,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "id"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "region"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "enable_managed_alerts", "true"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "alert_manager_url"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "contact_points.0.email", "test@example.com"), testAccCheckAlertManagerIDFormat(tt, "scaleway_cockpit_alert_manager.main"), @@ -206,7 +197,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource "scaleway_cockpit_alert_manager" "main" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true contact_points { email = "updated@example.com" @@ -241,7 +231,6 @@ func testAccCockpitAlertManagerConfigWithContacts(contactPoints []map[string]str resource "scaleway_cockpit_alert_manager" "alert_manager" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true %s } `, contactsConfig) @@ -390,3 +379,163 @@ func testAccCheckAlertManagerIDFormat(tt *acctest.TestTools, resourceName string return nil } } + +func TestAccCockpitAlertManager_WithPreconfiguredAlerts(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + CheckDestroy: testAccCockpitAlertManagerAndContactsDestroy(tt), + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_preconfigured" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable 2 specific preconfigured alerts (stable IDs) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7" # MySQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "region"), + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "alert_manager_url"), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "contact_points.0.email", "test@example.com"), + ), + }, + }, + }) +} + +func TestAccCockpitAlertManager_UpdatePreconfiguredAlerts(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + CheckDestroy: testAccCockpitAlertManagerAndContactsDestroy(tt), + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable a specific PostgreSQL alert (stable ID) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7" # PostgreSQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 1), + ), + }, + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable 2 specific alerts (stable IDs) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7" # MySQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 2), + ), + }, + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Disable all + preconfigured_alert_ids = [] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "0"), + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 0), + ), + }, + }, + }) +} + +func testAccCheckPreconfiguredAlertsCount(tt *acctest.TestTools, resourceName string, expectedCount int) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return errors.New("alert manager not found: " + resourceName) + } + + api := cockpit.NewRegionalAPI(meta.ExtractScwClient(tt.Meta)) + projectID := rs.Primary.Attributes["project_id"] + region := scw.Region(rs.Primary.Attributes["region"]) + + // List all preconfigured alerts + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + }, scw.WithAllPages()) + if err != nil { + return err + } + + // Count alerts that are enabled or enabling + enabledCount := 0 + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil { + if alert.RuleStatus == cockpit.AlertStatusEnabled || alert.RuleStatus == cockpit.AlertStatusEnabling { + enabledCount++ + } + } + } + + if enabledCount != expectedCount { + return fmt.Errorf("expected %d enabled preconfigured alerts, got %d", expectedCount, enabledCount) + } + + return nil + } +} diff --git a/internal/services/cockpit/preconfigured_alert_data_source.go b/internal/services/cockpit/preconfigured_alert_data_source.go new file mode 100644 index 0000000000..f42792da04 --- /dev/null +++ b/internal/services/cockpit/preconfigured_alert_data_source.go @@ -0,0 +1,175 @@ +package cockpit + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + cockpit "github.com/scaleway/scaleway-sdk-go/api/cockpit/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/verify" +) + +func DataSourceCockpitPreconfiguredAlert() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCockpitPreconfiguredAlertRead, + Schema: map[string]*schema.Schema{ + "project_id": account.ProjectIDSchema(), + "region": regional.Schema(), + "alerts": { + Type: schema.TypeList, + Computed: true, + Description: "List of preconfigured alerts", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the alert", + }, + "rule": { + Type: schema.TypeString, + Computed: true, + Description: "PromQL rule defining the alert condition", + }, + "duration": { + Type: schema.TypeString, + Computed: true, + Description: "Duration for which the alert must be active before firing", + }, + "rule_status": { + Type: schema.TypeString, + Computed: true, + Description: "Status of the alert (enabled, disabled, enabling, disabling)", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Current state of the alert (inactive, pending, firing)", + }, + "annotations": { + Type: schema.TypeMap, + Computed: true, + Description: "Annotations for the alert", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "preconfigured_rule_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the preconfigured rule", + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + Description: "Human readable name of the alert", + }, + "display_description": { + Type: schema.TypeString, + Computed: true, + Description: "Human readable description of the alert", + }, + "product_name": { + Type: schema.TypeString, + Computed: true, + Description: "Product associated with the alert", + }, + "product_family": { + Type: schema.TypeString, + Computed: true, + Description: "Family of the product associated with the alert", + }, + "data_source_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the data source containing the alert rule", + }, + }, + }, + }, + "data_source_id": { + Type: schema.TypeString, + Optional: true, + Description: "Filter alerts by data source ID", + ValidateDiagFunc: verify.IsUUID(), + }, + "rule_status": { + Type: schema.TypeString, + Optional: true, + Description: "Filter alerts by rule status (enabled, disabled)", + ValidateDiagFunc: verify.ValidateEnum[cockpit.AlertStatus](), + }, + }, + } +} + +func dataSourceCockpitPreconfiguredAlertRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + api, region, err := cockpitAPIWithRegion(d, m) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get("project_id").(string) + if projectID == "" { + defaultProjectID, err := getDefaultProjectID(ctx, m) + if err != nil { + return diag.FromErr(err) + } + projectID = defaultProjectID + } + + req := &cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + } + + if dataSourceID, ok := d.GetOk("data_source_id"); ok { + req.DataSourceID = scw.StringPtr(dataSourceID.(string)) + } + + if ruleStatus, ok := d.GetOk("rule_status"); ok { + status := cockpit.AlertStatus(ruleStatus.(string)) + req.RuleStatus = &status + } + + response, err := api.ListAlerts(req, scw.WithContext(ctx), scw.WithAllPages()) + if err != nil { + return diag.FromErr(err) + } + + alerts := make([]map[string]any, 0, len(response.Alerts)) + for _, alert := range response.Alerts { + alertMap := map[string]any{ + "name": alert.Name, + "rule": alert.Rule, + "duration": alert.Duration, + "rule_status": string(alert.RuleStatus), + "annotations": alert.Annotations, + "data_source_id": alert.DataSourceID, + } + + if alert.State != nil { + alertMap["state"] = string(*alert.State) + } + + if alert.PreconfiguredData != nil { + alertMap["preconfigured_rule_id"] = alert.PreconfiguredData.PreconfiguredRuleID + alertMap["display_name"] = alert.PreconfiguredData.DisplayName + alertMap["display_description"] = alert.PreconfiguredData.DisplayDescription + alertMap["product_name"] = alert.PreconfiguredData.ProductName + alertMap["product_family"] = alert.PreconfiguredData.ProductFamily + } + + alerts = append(alerts, alertMap) + } + + d.SetId(regional.NewIDString(region, projectID)) + _ = d.Set("project_id", projectID) + _ = d.Set("region", string(region)) + _ = d.Set("alerts", alerts) + + return nil +} + diff --git a/internal/services/cockpit/preconfigured_alert_data_source_test.go b/internal/services/cockpit/preconfigured_alert_data_source_test.go new file mode 100644 index 0000000000..7039e76c31 --- /dev/null +++ b/internal/services/cockpit/preconfigured_alert_data_source_test.go @@ -0,0 +1,74 @@ +package cockpit_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest" +) + +func TestAccDataSourceCockpitPreconfiguredAlert_Basic(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_preconfigured_alert_ds" + } + + resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id + } + + data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_cockpit.main.project_id + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair("data.scaleway_cockpit_preconfigured_alert.main", "project_id", "scaleway_account_project.project", "id"), + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.main", "alerts.#"), + ), + }, + }, + }) +} + +func TestAccDataSourceCockpitPreconfiguredAlert_WithFilters(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_preconfigured_alert_filters" + } + + resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id + } + + data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_cockpit.main.project_id + rule_status = "enabled" + } + + data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_cockpit.main.project_id + rule_status = "disabled" + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.enabled", "alerts.#"), + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.disabled", "alerts.#"), + ), + }, + }, + }) +} diff --git a/provider/sdkv2.go b/provider/sdkv2.go index d37440ca1c..c6e17e9a6d 100644 --- a/provider/sdkv2.go +++ b/provider/sdkv2.go @@ -274,6 +274,7 @@ func SDKProvider(config *Config) plugin.ProviderFunc { "scaleway_block_volume": block.DataSourceVolume(), "scaleway_cockpit": cockpit.DataSourceCockpit(), "scaleway_cockpit_grafana": cockpit.DataSourceCockpitGrafana(), + "scaleway_cockpit_preconfigured_alert": cockpit.DataSourceCockpitPreconfiguredAlert(), "scaleway_cockpit_source": cockpit.DataSourceCockpitSource(), "scaleway_cockpit_sources": cockpit.DataSourceCockpitSources(), "scaleway_config": scwconfig.DataSourceConfig(), diff --git a/templates/data-sources/cockpit_preconfigured_alert.md.tmpl b/templates/data-sources/cockpit_preconfigured_alert.md.tmpl new file mode 100644 index 0000000000..c3f84efa94 --- /dev/null +++ b/templates/data-sources/cockpit_preconfigured_alert.md.tmpl @@ -0,0 +1,100 @@ +--- +subcategory: "Cockpit" +page_title: "Scaleway: scaleway_cockpit_preconfigured_alert" +--- + +# Data Source: scaleway_cockpit_preconfigured_alert + +Gets information about preconfigured alert rules available in Scaleway Cockpit. + +Preconfigured alerts are ready-to-use alert rules that monitor common metrics for Scaleway services. +You can enable these alerts in your Alert Manager using the `scaleway_cockpit_alert_manager` resource. + +For more information, refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/) and [API documentation](https://www.scaleway.com/en/developers/api/cockpit/regional-api). + +## Example Usage + +### Basic usage + +```terraform +data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.main.alerts +} +``` + +### Filter by status + +```terraform +data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_account_project.project.id + rule_status = "enabled" +} + +data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_account_project.project.id + rule_status = "disabled" +} +``` + +### Use with Alert Manager + +```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific alerts by their preconfigured_rule_id + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" && alert.rule_status == "disabled" + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Argument Reference + +- `project_id` - (Optional) The ID of the project the alerts are associated with. If not provided, the default project configured in the provider is used. +- `region` - (Optional, defaults to provider region) The region in which the alerts exist. +- `data_source_id` - (Optional) Filter alerts by data source ID. +- `rule_status` - (Optional) Filter alerts by rule status. Valid values are `enabled` or `disabled`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the resource (project ID with region). +- `alerts` - List of preconfigured alerts. Each alert contains: + - `name` - Name of the alert rule. + - `rule` - PromQL expression defining the alert condition. + - `duration` - Duration for which the condition must be true before the alert fires (e.g., "5m"). + - `rule_status` - Status of the alert rule (`enabled`, `disabled`, `enabling`, `disabling`). + - `state` - Current state of the alert (`inactive`, `pending`, `firing`). + - `annotations` - Map of annotations attached to the alert. + - `preconfigured_rule_id` - Unique identifier of the preconfigured rule. Use this ID in `scaleway_cockpit_alert_manager` resource. + - `display_name` - Human-readable name of the alert. + - `display_description` - Human-readable description of the alert. + - `product_name` - Scaleway product associated with the alert (e.g., "instance", "rdb", "kubernetes"). + - `product_family` - Family of the product (e.g., "compute", "storage", "network"). + - `data_source_id` - ID of the data source containing the alert rule. + + diff --git a/templates/guides/migration_guide_cockpit_alert_manager.md b/templates/guides/migration_guide_cockpit_alert_manager.md new file mode 100644 index 0000000000..396fe44c60 --- /dev/null +++ b/templates/guides/migration_guide_cockpit_alert_manager.md @@ -0,0 +1,166 @@ +--- +page_title: "Cockpit Alert Manager Migration Guide" +--- + +# Cockpit Alert Manager Migration Guide + +This guide explains how to migrate from the deprecated `enable_managed_alerts` field to the new `preconfigured_alert_ids` field in the `scaleway_cockpit_alert_manager` resource. + +## Background + +The `enable_managed_alerts` field is being deprecated in favor of a more flexible approach using `preconfigured_alert_ids`. This change provides: + +- **Granular control**: Select specific alerts instead of enabling all managed alerts +- **Better visibility**: Explicitly declare which alerts are enabled in your Terraform configuration +- **Improved state management**: Terraform accurately tracks which alerts are active + +## Migration Steps + +### Before Migration (Deprecated) + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alerts@example.com" + } +} +``` + +### After Migration (Recommended) + +#### Step 1: List Available Preconfigured Alerts + +Use the data source to discover available alerts: + +```terraform +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.all.alerts +} +``` + +Run `terraform apply` and review the output to see available alerts. + +#### Step 2: Select Specific Alerts + +Choose the alerts you want to enable: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable specific alerts by product/family + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if contains(["PostgreSQL", "MySQL"], alert.product_name) + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +Or use specific alert IDs: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7", # MySQL Too Many Connections + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Filtering Alerts + +### By Product Name + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "Kubernetes" +] +``` + +### By Product Family + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Managed Databases" +] +``` + +### Multiple Criteria + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Load Balancer" && alert.product_name == "LB" +] +``` + +## Important Notes + +### Behavioral Changes + +- **No automatic alerts**: Unlike `enable_managed_alerts = true`, the API will not automatically enable additional alerts +- **Explicit configuration**: You must explicitly list all alerts you want to enable +- **State accuracy**: Terraform state will only track alerts you've configured + +### Compatibility + +- The deprecated `enable_managed_alerts` field will be removed in a future major version +- Both fields can coexist during migration, but `preconfigured_alert_ids` takes precedence +- If neither field is specified, no preconfigured alerts will be enabled + +## Troubleshooting + +### "Insufficient permissions" Error + +If you see permission errors when using the `scaleway_cockpit_preconfigured_alert` data source, ensure your IAM policy includes: + +```json +{ + "permission_sets": [ + { + "name": "CockpitManager", + "permissions": [ + "read:cockpit" + ] + } + ] +} +``` + +### Unexpected State Changes + +If Terraform shows unexpected changes to `preconfigured_alert_ids`: + +1. Verify the alert IDs still exist by querying the data source +2. Check that alerts are in `enabled` or `enabling` state +3. Ensure no manual changes were made outside Terraform + +## Additional Resources + +- [Cockpit Alert Manager Resource Documentation](../resources/cockpit_alert_manager.md) +- [Cockpit Preconfigured Alert Data Source Documentation](../data-sources/cockpit_preconfigured_alert.md) +- [Scaleway Cockpit Documentation](https://www.scaleway.com/en/docs/observability/cockpit/) + diff --git a/templates/resources/cockpit_alert_manager.md.tmpl b/templates/resources/cockpit_alert_manager.md.tmpl index 0b20f6a8f4..5b175f0468 100644 --- a/templates/resources/cockpit_alert_manager.md.tmpl +++ b/templates/resources/cockpit_alert_manager.md.tmpl @@ -13,23 +13,48 @@ Refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/obse ## Example Usage -### Enable the alert manager and configure managed alerts +### Enable preconfigured alerts (Recommended) -The following commands allow you to: - -- enable the alert manager in a Project named `tf_test_project` -- enable [managed alerts](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#managed-alerts) -- set up [contact points](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#contact-points) to receive alert notifications +Use preconfigured alerts to monitor your Scaleway resources with ready-to-use alert rules: ```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific preconfigured alerts + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" + ] + contact_points { + email = "alerts@example.com" + } +} +``` + +### Enable the alert manager with contact points + +```terraform resource "scaleway_account_project" "project" { name = "tf_test_project" } resource "scaleway_cockpit_alert_manager" "alert_manager" { - project_id = scaleway_account_project.project.id - enable_managed_alerts = true + project_id = scaleway_account_project.project.id contact_points { email = "alert1@example.com" @@ -41,13 +66,33 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { } ``` +### Legacy: Enable managed alerts (Deprecated) + +~> **Deprecated:** The `enable_managed_alerts` field is deprecated. Use `preconfigured_alert_ids` instead. + +```terraform +resource "scaleway_account_project" "project" { + name = "tf_test_project" +} + +resource "scaleway_cockpit_alert_manager" "alert_manager" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alert@example.com" + } +} +``` + ## Argument Reference This section lists the arguments that are supported: -- `enable_managed_alerts` - (Optional, Boolean) Specifies whether the alert manager should be enabled. Defaults to true. -- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key email. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. +- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. - `region` - (Defaults to the region specified in the [provider configuration](../index.md#arguments-reference)) The [region](../guides/regions_and_zones.md#regions) where the [alert manager](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#alert-manager) should be enabled. From 8953152540c2972896cdf140c40b5877643b7a2c Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Fri, 21 Nov 2025 09:21:17 +0100 Subject: [PATCH 2/3] feat(cockpit): add default_preconfigured_alert_ids field to track API defaults --- docs/resources/cockpit_alert_manager.md | 3 +- internal/services/cockpit/alert_manager.go | 92 +++++++++++-------- .../services/cockpit/alert_manager_test.go | 16 ++++ .../resources/cockpit_alert_manager.md.tmpl | 3 +- 4 files changed, 74 insertions(+), 40 deletions(-) diff --git a/docs/resources/cockpit_alert_manager.md b/docs/resources/cockpit_alert_manager.md index c32aa501f0..963d1c9bc3 100644 --- a/docs/resources/cockpit_alert_manager.md +++ b/docs/resources/cockpit_alert_manager.md @@ -89,7 +89,7 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { This section lists the arguments that are supported: -- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable explicitly. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. - `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. - `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. @@ -100,6 +100,7 @@ This section lists the arguments that are supported: In addition to all arguments above, the following attributes are exported: - `alert_manager_url` - The URL of the alert manager. +- `default_preconfigured_alert_ids` - (Set of String) A set of preconfigured alert rule IDs that are enabled automatically by the API when the alert manager is activated. This is a computed field that shows which alerts the API enables by default. ## Import diff --git a/internal/services/cockpit/alert_manager.go b/internal/services/cockpit/alert_manager.go index 7def2634b0..41898089f7 100644 --- a/internal/services/cockpit/alert_manager.go +++ b/internal/services/cockpit/alert_manager.go @@ -36,7 +36,13 @@ func ResourceCockpitAlertManager() *schema.Resource { "preconfigured_alert_ids": { Type: schema.TypeSet, Optional: true, - Description: "List of preconfigured alert rule IDs to enable. Use the scaleway_cockpit_preconfigured_alert data source to list available alerts.", + Description: "List of preconfigured alert rule IDs to enable explicitly. Use the scaleway_cockpit_preconfigured_alert data source to list available alerts.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "default_preconfigured_alert_ids": { + Type: schema.TypeSet, + Computed: true, + Description: "List of preconfigured alert rule IDs enabled automatically by the API when alert manager is activated.", Elem: &schema.Schema{Type: schema.TypeString}, }, "contact_points": { @@ -94,8 +100,8 @@ func ResourceCockpitAlertManagerCreate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available - // For now, we continue without waiting as the Read function handles enabling/enabled states + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available + // For now, we continue without waiting as the Read function handles enabling/enabled states } } @@ -158,49 +164,59 @@ func ResourceCockpitAlertManagerRead(ctx context.Context, d *schema.ResourceData _ = d.Set("alert_manager_url", alertManager.AlertManagerURL) _ = d.Set("project_id", projectID) - // Get enabled preconfigured alerts - // Only track the alerts that were explicitly requested by the user in the configuration. - // The API may enable additional alerts automatically, but we ignore those to prevent perpetual drift. - // We set the field based on what was requested in the config, not what the API returns. - if v, ok := d.GetOk("preconfigured_alert_ids"); ok { - requestedIDs := expandStringSet(v.(*schema.Set)) - - // List all preconfigured alerts to verify their status - alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ - Region: region, - ProjectID: projectID, - IsPreconfigured: scw.BoolPtr(true), - }, scw.WithContext(ctx), scw.WithAllPages()) - if err != nil { - return diag.FromErr(err) + // Get enabled preconfigured alerts and separate user-requested vs API-default alerts + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + }, scw.WithContext(ctx), scw.WithAllPages()) + if err != nil { + return diag.FromErr(err) + } + + // Build a map of alert statuses + alertStatusMap := make(map[string]cockpit.AlertStatus) + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { + alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus } + } - // Build a map of alert statuses - alertStatusMap := make(map[string]cockpit.AlertStatus) - for _, alert := range alerts.Alerts { - if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { - alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus - } + // Separate user-requested alerts from API-default alerts + var userRequestedIDs []string + var defaultEnabledIDs []string + + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + requestedIDs := expandStringSet(v.(*schema.Set)) + requestedMap := make(map[string]bool) + for _, id := range requestedIDs { + requestedMap[id] = true } - // Keep the requested IDs that are enabled/enabling - // This ensures we only track what the user explicitly configured - var enabledRequestedIDs []string - for _, requestedID := range requestedIDs { - if status, found := alertStatusMap[requestedID]; found { - // Include if enabled or enabling - if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { - enabledRequestedIDs = append(enabledRequestedIDs, requestedID) + // Check all enabled/enabling alerts + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + if requestedMap[ruleID] { + // This alert was explicitly requested by the user + userRequestedIDs = append(userRequestedIDs, ruleID) + } else { + // This alert was enabled automatically by the API + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) } } } - - _ = d.Set("preconfigured_alert_ids", enabledRequestedIDs) } else { - // No alerts requested, set empty list - _ = d.Set("preconfigured_alert_ids", []string{}) + // No alerts explicitly requested, all enabled alerts are API defaults + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } + } } + _ = d.Set("preconfigured_alert_ids", userRequestedIDs) + _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region, ProjectID: projectID, @@ -254,7 +270,7 @@ func ResourceCockpitAlertManagerUpdate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - // Note: Waiting for alerts to be disabled will be handled by SDK waiters when available + // Note: Waiting for alerts to be disabled will be handled by SDK waiters when available } // IDs to enable: in new but not in old @@ -269,7 +285,7 @@ func ResourceCockpitAlertManagerUpdate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available } } diff --git a/internal/services/cockpit/alert_manager_test.go b/internal/services/cockpit/alert_manager_test.go index 755763f06b..7e5fe7d8b8 100644 --- a/internal/services/cockpit/alert_manager_test.go +++ b/internal/services/cockpit/alert_manager_test.go @@ -413,6 +413,11 @@ func TestAccCockpitAlertManager_WithPreconfiguredAlerts(t *testing.T) { resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "alert_manager_url"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "contact_points.0.email", "test@example.com"), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "2"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), ), }, }, @@ -449,6 +454,10 @@ func TestAccCockpitAlertManager_UpdatePreconfiguredAlerts(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 1), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "1"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), ), }, { @@ -473,6 +482,11 @@ func TestAccCockpitAlertManager_UpdatePreconfiguredAlerts(t *testing.T) { `, Check: resource.ComposeTestCheckFunc( testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 2), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "2"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), ), }, { @@ -495,6 +509,8 @@ func TestAccCockpitAlertManager_UpdatePreconfiguredAlerts(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "0"), testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 0), + // Default alerts might still be present even when user requests none + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), ), }, }, diff --git a/templates/resources/cockpit_alert_manager.md.tmpl b/templates/resources/cockpit_alert_manager.md.tmpl index 5b175f0468..4299033a6e 100644 --- a/templates/resources/cockpit_alert_manager.md.tmpl +++ b/templates/resources/cockpit_alert_manager.md.tmpl @@ -90,7 +90,7 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { This section lists the arguments that are supported: -- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable explicitly. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. - `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. - `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. @@ -101,6 +101,7 @@ This section lists the arguments that are supported: In addition to all arguments above, the following attributes are exported: - `alert_manager_url` - The URL of the alert manager. +- `default_preconfigured_alert_ids` - (Set of String) A set of preconfigured alert rule IDs that are enabled automatically by the API when the alert manager is activated. This is a computed field that shows which alerts the API enables by default. ## Import From 150fd72a86fc06fb3b58b917142f5d9de132fd5f Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Fri, 21 Nov 2025 09:30:08 +0100 Subject: [PATCH 3/3] fix(cockpit): handle permission errors gracefully in alert manager read --- internal/services/cockpit/alert_manager.go | 77 +++++++++++----------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/internal/services/cockpit/alert_manager.go b/internal/services/cockpit/alert_manager.go index 41898089f7..3db14ff9ed 100644 --- a/internal/services/cockpit/alert_manager.go +++ b/internal/services/cockpit/alert_manager.go @@ -165,57 +165,60 @@ func ResourceCockpitAlertManagerRead(ctx context.Context, d *schema.ResourceData _ = d.Set("project_id", projectID) // Get enabled preconfigured alerts and separate user-requested vs API-default alerts + // Handle permission errors gracefully to allow the resource to work without cockpit read permissions + var userRequestedIDs []string + var defaultEnabledIDs []string + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ Region: region, ProjectID: projectID, IsPreconfigured: scw.BoolPtr(true), }, scw.WithContext(ctx), scw.WithAllPages()) if err != nil { - return diag.FromErr(err) - } - - // Build a map of alert statuses - alertStatusMap := make(map[string]cockpit.AlertStatus) - for _, alert := range alerts.Alerts { - if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { - alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus + // If we can't read alerts (e.g., permission denied), just set empty arrays + // This allows the resource to work even without cockpit read permissions + _ = d.Set("preconfigured_alert_ids", userRequestedIDs) + _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + } else { + // Build a map of alert statuses + alertStatusMap := make(map[string]cockpit.AlertStatus) + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { + alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus + } } - } - - // Separate user-requested alerts from API-default alerts - var userRequestedIDs []string - var defaultEnabledIDs []string - if v, ok := d.GetOk("preconfigured_alert_ids"); ok { - requestedIDs := expandStringSet(v.(*schema.Set)) - requestedMap := make(map[string]bool) - for _, id := range requestedIDs { - requestedMap[id] = true - } + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + requestedIDs := expandStringSet(v.(*schema.Set)) + requestedMap := make(map[string]bool) + for _, id := range requestedIDs { + requestedMap[id] = true + } - // Check all enabled/enabling alerts - for ruleID, status := range alertStatusMap { - if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { - if requestedMap[ruleID] { - // This alert was explicitly requested by the user - userRequestedIDs = append(userRequestedIDs, ruleID) - } else { - // This alert was enabled automatically by the API - defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + // Check all enabled/enabling alerts + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + if requestedMap[ruleID] { + // This alert was explicitly requested by the user + userRequestedIDs = append(userRequestedIDs, ruleID) + } else { + // This alert was enabled automatically by the API + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } } } - } - } else { - // No alerts explicitly requested, all enabled alerts are API defaults - for ruleID, status := range alertStatusMap { - if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { - defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } else { + // No alerts explicitly requested, all enabled alerts are API defaults + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } } } - } - _ = d.Set("preconfigured_alert_ids", userRequestedIDs) - _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + _ = d.Set("preconfigured_alert_ids", userRequestedIDs) + _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + } contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region,