Skip to content
7 changes: 7 additions & 0 deletions .changelog/3795.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
data-source/mongodbatlas_alert_configuration: Adds `severity_override` attribute
```

```release-note:enhancement
resource/mongodbatlas_alert_configuration: Adds `severity_override` attribute
```
1 change: 1 addition & 0 deletions contributing/testing-best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- `Basic import tests` are done as the last step in the `basic acceptance tests`, not as a different test, e.g. [basicTestCase](https://github.com/mongodb/terraform-provider-mongodbatlas/blob/66c44e62c9afe04ffe8be0dbccaec682bab830e6/internal/service/searchindex/resource_search_index_test.go#L211). Exceptions apply for more specific import tests, e.g. testing with incorrect IDs. [Import tests](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/import#resource-acceptance-testing-implementation) verify that the [Terraform Import](https://developer.hashicorp.com/terraform/cli/import) functionality is working fine.
- Data sources are tested in the same tests as the resources, e.g. [commonChecks](https://github.com/mongodb/terraform-provider-mongodbatlas/blob/66c44e62c9afe04ffe8be0dbccaec682bab830e6/internal/service/searchindex/resource_search_index_test.go#L262-L263).
- Helper functions such as `resource.TestCheckTypeSetElemNestedAttrs` or `resource.TestCheckTypeSetElemAttr` can be used to check resource and data source attributes more easily, e.g. [resource_serverless_instance_test.go](https://github.com/mongodb/terraform-provider-mongodbatlas/blob/66c44e62c9afe04ffe8be0dbccaec682bab830e6/internal/service/serverlessinstance/resource_serverless_instance_test.go#L61).
- Note: before running the acceptance tests, make sure you set the environment variable `TF_ACC=1`.

### Cloud Gov tests

Expand Down
8 changes: 8 additions & 0 deletions internal/service/alertconfiguration/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type TFAlertConfigurationDSModel struct {
EventType types.String `tfsdk:"event_type"`
Created types.String `tfsdk:"created"`
Updated types.String `tfsdk:"updated"`
SeverityOverride types.String `tfsdk:"severity_override"`
Matcher []TfMatcherModel `tfsdk:"matcher"`
MetricThresholdConfig []TfMetricThresholdConfigModel `tfsdk:"metric_threshold_config"`
ThresholdConfig []TfThresholdConfigModel `tfsdk:"threshold_config"`
Expand Down Expand Up @@ -242,6 +243,9 @@ var alertConfigDSSchemaAttributes = map[string]schema.Attribute{
},
},
},
"severity_override": schema.StringAttribute{
Optional: true,
},
}

func (d *alertConfigurationDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
Expand Down Expand Up @@ -339,6 +343,10 @@ func outputAlertConfigurationResourceHcl(label string, alert *admin.GroupAlertsC
appendBlockWithCtyValues(resource, "notification", []string{}, convertNotificationToCtyValues(&notifications[i]))
}

if alert.SeverityOverride != nil {
resource.SetAttributeValue("severity_override", cty.StringVal(*alert.SeverityOverride))
}

return string(f.Bytes())
}

Expand Down
2 changes: 2 additions & 0 deletions internal/service/alertconfiguration/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func NewTFAlertConfigurationModel(apiRespConfig *admin.GroupAlertsConfig, currSt
ThresholdConfig: NewTFThresholdConfigModel(apiRespConfig.Threshold, currState.ThresholdConfig),
Notification: NewTFNotificationModelList(apiRespConfig.GetNotifications(), currState.Notification),
Matcher: NewTFMatcherModelList(apiRespConfig.GetMatchers(), currState.Matcher),
SeverityOverride: types.StringPointerValue(apiRespConfig.SeverityOverride),
}
}

Expand Down Expand Up @@ -298,6 +299,7 @@ func NewTfAlertConfigurationDSModel(apiRespConfig *admin.GroupAlertsConfig, proj
ThresholdConfig: NewTFThresholdConfigModel(apiRespConfig.Threshold, []TfThresholdConfigModel{}),
Notification: NewTFNotificationModelList(apiRespConfig.GetNotifications(), []TfNotificationModel{}),
Matcher: NewTFMatcherModelList(apiRespConfig.GetMatchers(), []TfMatcherModel{}),
SeverityOverride: types.StringPointerValue(apiRespConfig.SeverityOverride),
}
}

Expand Down
85 changes: 84 additions & 1 deletion internal/service/alertconfiguration/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,40 @@ func TestAlertConfigurationSDKToTFModel(t *testing.T) {
Enabled: types.BoolValue(true),
},
},
{
name: "Complete SKD response with SeverityOverride",
SDKResp: &admin.GroupAlertsConfig{
Enabled: admin.PtrBool(true),
EventTypeName: admin.PtrString("EventType"),
GroupId: admin.PtrString("projectId"),
Id: admin.PtrString("alertConfigurationId"),
SeverityOverride: admin.PtrString("WARNING"),
},
currentStateAlertConfiguration: &alertconfiguration.TfAlertConfigurationRSModel{
ID: types.StringValue("id"),
ProjectID: types.StringValue("projectId"),
AlertConfigurationID: types.StringValue("alertConfigurationId"),
EventType: types.StringValue("EventType"),
Matcher: []alertconfiguration.TfMatcherModel{},
MetricThresholdConfig: []alertconfiguration.TfMetricThresholdConfigModel{},
ThresholdConfig: []alertconfiguration.TfThresholdConfigModel{},
Notification: []alertconfiguration.TfNotificationModel{},
Enabled: types.BoolValue(true),
SeverityOverride: types.StringValue("WARNING"),
},
expectedTFModel: alertconfiguration.TfAlertConfigurationRSModel{
ID: types.StringValue("id"),
ProjectID: types.StringValue("projectId"),
AlertConfigurationID: types.StringValue("alertConfigurationId"),
EventType: types.StringValue("EventType"),
Matcher: []alertconfiguration.TfMatcherModel{},
MetricThresholdConfig: []alertconfiguration.TfMetricThresholdConfigModel{},
ThresholdConfig: []alertconfiguration.TfThresholdConfigModel{},
Notification: []alertconfiguration.TfNotificationModel{},
Enabled: types.BoolValue(true),
SeverityOverride: types.StringValue("WARNING"),
},
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -490,9 +524,58 @@ func TestAlertConfigurationSdkToDSModelList(t *testing.T) {
{
Type: types.StringValue("resource_hcl"),
Label: types.StringValue("EventType_0"),
Value: types.StringValue("resource \"mongodbatlas_alert_configuration\" \"EventType_0\" {\n project_id = \"projectId\"\n event_type = \"EventType\"\n enabled = true\n}\n"),
Value: types.StringValue(`resource "mongodbatlas_alert_configuration" "EventType_0" {
project_id = "projectId"
event_type = "EventType"
enabled = true
}
`),
},
},
},
},
},
{
name: "Complete SDK model with SeverityOverride",
alerts: []admin.GroupAlertsConfig{
{
Enabled: admin.PtrBool(true),
EventTypeName: admin.PtrString("EventType"),
GroupId: admin.PtrString("projectId"),
Id: admin.PtrString("alertConfigurationId"),
SeverityOverride: admin.PtrString("WARNING"),
},
},
projectID: "projectId",
definedOutputs: []string{"resource_hcl"},
expectedTfModel: []alertconfiguration.TFAlertConfigurationDSModel{
{
ID: types.StringValue(conversion.EncodeStateID(map[string]string{
"id": "alertConfigurationId",
"project_id": "projectId",
})),
ProjectID: types.StringValue("projectId"),
AlertConfigurationID: types.StringValue("alertConfigurationId"),
EventType: types.StringValue("EventType"),
Enabled: types.BoolValue(true),
Matcher: []alertconfiguration.TfMatcherModel{},
MetricThresholdConfig: []alertconfiguration.TfMetricThresholdConfigModel{},
ThresholdConfig: []alertconfiguration.TfThresholdConfigModel{},
Notification: []alertconfiguration.TfNotificationModel{},
Output: []alertconfiguration.TfAlertConfigurationOutputModel{
{
Type: types.StringValue("resource_hcl"),
Label: types.StringValue("EventType_0"),
Value: types.StringValue(`resource "mongodbatlas_alert_configuration" "EventType_0" {
project_id = "projectId"
event_type = "EventType"
enabled = true
severity_override = "WARNING"
}
`),
},
},
SeverityOverride: types.StringValue("WARNING"),
},
},
},
Expand Down
19 changes: 14 additions & 5 deletions internal/service/alertconfiguration/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type TfAlertConfigurationRSModel struct {
EventType types.String `tfsdk:"event_type"`
Created types.String `tfsdk:"created"`
Updated types.String `tfsdk:"updated"`
SeverityOverride types.String `tfsdk:"severity_override"`
Matcher []TfMatcherModel `tfsdk:"matcher"`
MetricThresholdConfig []TfMetricThresholdConfigModel `tfsdk:"metric_threshold_config"`
ThresholdConfig []TfThresholdConfigModel `tfsdk:"threshold_config"`
Expand Down Expand Up @@ -154,6 +155,9 @@ func (r *alertConfigurationRS) Schema(ctx context.Context, req resource.SchemaRe
boolplanmodifier.UseStateForUnknown(),
},
},
"severity_override": schema.StringAttribute{
Optional: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

just to double check: This is never returned by the API unless defined by the user? How do the user reset the attribute?

Copy link
Contributor Author

@ciprian-tibulca ciprian-tibulca Oct 24, 2025

Choose a reason for hiding this comment

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

This is never returned by the API unless defined by the user?

correct

How do the user reset the attribute?

as of now, this flag cannot be cleared (if not set in the PUT request body it won't be reset). Same happens from UI too, I will open a ticket with the owning team.
I added a note in the docs about explicitly setting it to ERROR (the default).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

},
},
Blocks: map[string]schema.Block{
"matcher": schema.ListNestedBlock{
Expand Down Expand Up @@ -381,11 +385,12 @@ func (r *alertConfigurationRS) Create(ctx context.Context, req resource.CreateRe
projectID := alertConfigPlan.ProjectID.ValueString()

apiReq := &admin.GroupAlertsConfig{
EventTypeName: alertConfigPlan.EventType.ValueStringPointer(),
Enabled: alertConfigPlan.Enabled.ValueBoolPointer(),
Matchers: NewMatcherList(alertConfigPlan.Matcher),
MetricThreshold: NewMetricThreshold(alertConfigPlan.MetricThresholdConfig),
Threshold: NewThreshold(alertConfigPlan.ThresholdConfig),
EventTypeName: alertConfigPlan.EventType.ValueStringPointer(),
Enabled: alertConfigPlan.Enabled.ValueBoolPointer(),
Matchers: NewMatcherList(alertConfigPlan.Matcher),
MetricThreshold: NewMetricThreshold(alertConfigPlan.MetricThresholdConfig),
Threshold: NewThreshold(alertConfigPlan.ThresholdConfig),
SeverityOverride: alertConfigPlan.SeverityOverride.ValueStringPointer(),
}

notifications, err := NewNotificationList(alertConfigPlan.Notification)
Expand Down Expand Up @@ -488,6 +493,10 @@ func (r *alertConfigurationRS) Update(ctx context.Context, req resource.UpdateRe
apiReq.Matchers = NewMatcherList(alertConfigPlan.Matcher)
}

if !alertConfigPlan.SeverityOverride.Equal(alertConfigState.SeverityOverride) {
apiReq.SeverityOverride = alertConfigPlan.SeverityOverride.ValueStringPointer()
}

// Always refresh structure to handle service keys being obfuscated coming back from read API call
notifications, err := NewNotificationList(alertConfigPlan.Notification)
if err != nil {
Expand Down
52 changes: 52 additions & 0 deletions internal/service/alertconfiguration/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestAccConfigRSAlertConfiguration_basic(t *testing.T) {
checkExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "project_id", projectID),
resource.TestCheckResourceAttr(resourceName, "notification.#", "2"),
resource.TestCheckNoResourceAttr(resourceName, "severity_override"),
// Data source checks
checkExists(dataSourceName),
resource.TestCheckResourceAttr(dataSourceName, "project_id", projectID),
Expand All @@ -47,6 +48,7 @@ func TestAccConfigRSAlertConfiguration_basic(t *testing.T) {
resource.TestCheckResourceAttr(dataSourceName, "matcher.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "metric_threshold_config.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "threshold_config.#", "0"),
resource.TestCheckNoResourceAttr(dataSourceName, "severity_override"),
),
},
{
Expand All @@ -55,6 +57,7 @@ func TestAccConfigRSAlertConfiguration_basic(t *testing.T) {
checkExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "project_id", projectID),
resource.TestCheckResourceAttr(resourceName, "notification.#", "2"),
resource.TestCheckNoResourceAttr(resourceName, "severity_override"),
// Data source checks
checkExists(dataSourceName),
resource.TestCheckResourceAttr(dataSourceName, "project_id", projectID),
Expand All @@ -63,6 +66,7 @@ func TestAccConfigRSAlertConfiguration_basic(t *testing.T) {
resource.TestCheckResourceAttr(dataSourceName, "matcher.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "metric_threshold_config.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "threshold_config.#", "0"),
resource.TestCheckNoResourceAttr(dataSourceName, "severity_override"),
),
},
{
Expand Down Expand Up @@ -566,6 +570,32 @@ func TestAccConfigRSAlertConfiguration_withVictorOps(t *testing.T) {
})
}

func TestAccConfigRSAlertConfiguration_withSeverityOverride(t *testing.T) {
var (
projectID = acc.ProjectIDExecution(t)
)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acc.PreCheckBasic(t) },
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories,
CheckDestroy: checkDestroy(),
Steps: []resource.TestStep{
{
Config: configWithSeverityOverride(projectID, "WARNING"),
Check: resource.ComposeAggregateTestCheckFunc(
Comment on lines 582 to 585
Copy link
Member

Choose a reason for hiding this comment

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

We can add an additional step modifying the value, and also capture behaviour of what happens when attribute is removed (related to Espen thread).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the step, byt it's currently commented (due to https://jira.mongodb.org/browse/CLOUDP-353933)

checkExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "project_id", projectID),
resource.TestCheckResourceAttr(resourceName, "severity_override", "WARNING"),
// Data source checks
checkExists(dataSourceName),
resource.TestCheckResourceAttr(dataSourceName, "project_id", projectID),
resource.TestCheckResourceAttr(dataSourceName, "severity_override", "WARNING"),
),
},
},
})
}

func checkExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down Expand Up @@ -1039,6 +1069,28 @@ func configWithEmptyOptionalBlocks(projectID string) string {
`, projectID)
}

func configWithSeverityOverride(projectID, severity string) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
project_id = %[1]q
enabled = true
event_type = "NO_PRIMARY"
severity_override = %[2]q

notification {
type_name = "EMAIL"
interval_min = 60
email_address = "[email protected]"
}
}

data "mongodbatlas_alert_configuration" "test" {
project_id = mongodbatlas_alert_configuration.test.project_id
alert_configuration_id = mongodbatlas_alert_configuration.test.id
}
`, projectID, severity)
}

func TestAccConfigDSAlertConfiguration_withOutput(t *testing.T) {
var (
projectID = acc.ProjectIDExecution(t)
Expand Down