diff --git a/docs/resources/contact_point.md b/docs/resources/contact_point.md
index 058b2f72a..d4c1d7906 100644
--- a/docs/resources/contact_point.md
+++ b/docs/resources/contact_point.md
@@ -50,6 +50,7 @@ resource "grafana_contact_point" "my_contact_point" {
- `googlechat` (Block Set) A contact point that sends notifications to Google Chat. (see [below for nested schema](#nestedblock--googlechat))
- `kafka` (Block Set) A contact point that publishes notifications to Apache Kafka topics. (see [below for nested schema](#nestedblock--kafka))
- `line` (Block Set) A contact point that sends notifications to LINE.me. (see [below for nested schema](#nestedblock--line))
+- `mqtt` (Block Set) A contact point that sends notifications to an MQTT broker. (see [below for nested schema](#nestedblock--mqtt))
- `oncall` (Block Set) A contact point that sends notifications to Grafana On-Call. (see [below for nested schema](#nestedblock--oncall))
- `opsgenie` (Block Set) A contact point that sends notifications to OpsGenie. (see [below for nested schema](#nestedblock--opsgenie))
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
@@ -212,6 +213,42 @@ Read-Only:
- `uid` (String) The UID of the contact point.
+
+### Nested Schema for `mqtt`
+
+Required:
+
+- `broker_url` (String) The URL of the MQTT broker.
+- `topic` (String) The topic to publish messages to.
+
+Optional:
+
+- `client_id` (String) The client ID to use when connecting to the broker.
+- `disable_resolve_message` (Boolean) Whether to disable sending resolve messages. Defaults to `false`.
+- `message_format` (String) The format of the message to send. Supported values are `json` and `text`.
+- `password` (String, Sensitive) The password to use when connecting to the broker.
+- `qos` (Number) The quality of service to use when sending messages. Supported values are 0, 1, and 2. Defaults to `0`.
+- `retain` (Boolean) Whether to retain messages on the broker. Defaults to `false`.
+- `settings` (Map of String, Sensitive) Additional custom properties to attach to the notifier. Defaults to `map[]`.
+- `tls_config` (Block Set) TLS configuration for the connection. (see [below for nested schema](#nestedblock--mqtt--tls_config))
+- `username` (String) The username to use when connecting to the broker.
+
+Read-Only:
+
+- `uid` (String) The UID of the contact point.
+
+
+### Nested Schema for `mqtt.tls_config`
+
+Optional:
+
+- `ca_certificate` (String, Sensitive) The CA certificate to use when verifying the server's certificate.
+- `client_certificate` (String, Sensitive) The client certificate to use when connecting to the server.
+- `client_key` (String, Sensitive) The client key to use when connecting to the server.
+- `insecure_skip_verify` (Boolean) Whether to skip verification of the server's certificate chain and host name. Defaults to `false`.
+
+
+
### Nested Schema for `oncall`
diff --git a/examples/resources/grafana_contact_point/_acc_receiver_types.tf b/examples/resources/grafana_contact_point/_acc_receiver_types.tf
index 752b5dda2..5c2573983 100644
--- a/examples/resources/grafana_contact_point/_acc_receiver_types.tf
+++ b/examples/resources/grafana_contact_point/_acc_receiver_types.tf
@@ -54,6 +54,23 @@ resource "grafana_contact_point" "receiver_types" {
description = "description"
}
+ mqtt {
+ broker_url = "tcp://localhost:1883"
+ client_id = "client_id"
+ topic = "grafana/alerts"
+ message_format = "json"
+ username = "username"
+ password = "password"
+ qos = 1
+ retain = true
+ tls_config {
+ insecure_skip_verify = true
+ ca_certificate = "ca_cert"
+ client_certificate = "client_cert"
+ client_key = "client"
+ }
+ }
+
opsgenie {
url = "http://opsgenie-api"
api_key = "token"
diff --git a/examples/resources/grafana_contact_point/_acc_receiver_types_11_3.tf b/examples/resources/grafana_contact_point/_acc_receiver_types_11_3.tf
new file mode 100644
index 000000000..dffeab764
--- /dev/null
+++ b/examples/resources/grafana_contact_point/_acc_receiver_types_11_3.tf
@@ -0,0 +1,20 @@
+resource "grafana_contact_point" "receiver_types" {
+ name = "Receiver Types since v11.3"
+
+ mqtt {
+ broker_url = "tcp://localhost:1883"
+ client_id = "grafana"
+ topic = "grafana/alerts"
+ message_format = "json"
+ username = "user"
+ password = "password123"
+ qos = 1
+ retain = true
+ tls_config {
+ insecure_skip_verify = true
+ ca_certificate = "ca_cert"
+ client_certificate = "client_cert"
+ client_key = "client_key"
+ }
+ }
+}
diff --git a/internal/resources/grafana/resource_alerting_contact_point.go b/internal/resources/grafana/resource_alerting_contact_point.go
index 9f43d236c..0eac8352a 100644
--- a/internal/resources/grafana/resource_alerting_contact_point.go
+++ b/internal/resources/grafana/resource_alerting_contact_point.go
@@ -28,6 +28,7 @@ var (
googleChatNotifier{},
kafkaNotifier{},
lineNotifier{},
+ mqttNotifier{},
oncallNotifier{},
opsGenieNotifier{},
pagerDutyNotifier{},
diff --git a/internal/resources/grafana/resource_alerting_contact_point_notifiers.go b/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
index fee1ef7e3..f10913570 100644
--- a/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
+++ b/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
@@ -619,6 +619,184 @@ func (o lineNotifier) unpack(raw interface{}, name string) *models.EmbeddedConta
}
}
+type mqttNotifier struct{}
+
+var _ notifier = (*mqttNotifier)(nil)
+
+func (o mqttNotifier) meta() notifierMeta {
+ return notifierMeta{
+ field: "mqtt",
+ typeStr: "mqtt",
+ desc: "A contact point that sends notifications to an MQTT broker.",
+ secureFields: []string{"password"},
+ }
+}
+
+func (o mqttNotifier) schema() *schema.Resource {
+ r := commonNotifierResource()
+ r.Schema["broker_url"] = &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The URL of the MQTT broker.",
+ }
+ r.Schema["topic"] = &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The topic to publish messages to.",
+ }
+ r.Schema["client_id"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The client ID to use when connecting to the broker.",
+ }
+ r.Schema["message_format"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ ValidateFunc: validation.StringInSlice([]string{"json", "text"}, false),
+ Description: "The format of the message to send. Supported values are `json` and `text`.",
+ }
+ r.Schema["username"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The username to use when connecting to the broker.",
+ }
+ r.Schema["password"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ Description: "The password to use when connecting to the broker.",
+ }
+ r.Schema["qos"] = &schema.Schema{
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: 0,
+ ValidateFunc: validation.IntBetween(0, 2),
+ Description: "The quality of service to use when sending messages. Supported values are 0, 1, and 2.",
+ }
+ r.Schema["retain"] = &schema.Schema{
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Whether to retain messages on the broker.",
+ }
+
+ r.Schema["tls_config"] = &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ Description: "TLS configuration for the connection.",
+ Elem: tlsConfig{}.schema(),
+ }
+
+ return r
+}
+
+func (o mqttNotifier) pack(p *models.EmbeddedContactPoint, data *schema.ResourceData) (interface{}, error) {
+ notifier := packCommonNotifierFields(p)
+ settings := p.Settings.(map[string]interface{})
+
+ packNotifierStringField(&settings, ¬ifier, "brokerUrl", "broker_url")
+ packNotifierStringField(&settings, ¬ifier, "topic", "topic")
+ packNotifierStringField(&settings, ¬ifier, "clientId", "client_id")
+ packNotifierStringField(&settings, ¬ifier, "messageFormat", "message_format")
+ packNotifierStringField(&settings, ¬ifier, "username", "username")
+ if v, ok := settings["insecureSkipVerify"]; ok && v != nil {
+ notifier["insecure_skip_verify"] = v.(bool)
+ delete(settings, "insecureSkipVerify")
+ }
+
+ packSecureFields(notifier, getNotifierConfigFromStateWithUID(data, o, p.UID), o.meta().secureFields)
+
+ notifier["settings"] = packSettings(p)
+ return notifier, nil
+}
+
+func (o mqttNotifier) unpack(raw interface{}, name string) *models.EmbeddedContactPoint {
+ json := raw.(map[string]interface{})
+ uid, disableResolve, settings := unpackCommonNotifierFields(json)
+
+ unpackNotifierStringField(&json, &settings, "broker_url", "brokerUrl")
+ unpackNotifierStringField(&json, &settings, "topic", "topic")
+ unpackNotifierStringField(&json, &settings, "client_id", "clientId")
+ unpackNotifierStringField(&json, &settings, "message_format", "messageFormat")
+ unpackNotifierStringField(&json, &settings, "username", "username")
+ unpackNotifierStringField(&json, &settings, "password", "password")
+ if v, ok := json["insecure_skip_verify"]; ok && v != nil {
+ settings["insecureSkipVerify"] = v.(bool)
+ }
+
+ return &models.EmbeddedContactPoint{
+ UID: uid,
+ Name: name,
+ Type: common.Ref(o.meta().typeStr),
+ DisableResolveMessage: disableResolve,
+ Settings: settings,
+ }
+}
+
+type tlsConfig struct{}
+
+func (t tlsConfig) schema() *schema.Resource {
+ r := &schema.Resource{
+ Schema: make(map[string]*schema.Schema),
+ }
+
+ r.Schema["insecure_skip_verify"] = &schema.Schema{
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Whether to skip verification of the server's certificate chain and host name.",
+ }
+ r.Schema["ca_certificate"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ Description: "The CA certificate to use when verifying the server's certificate.",
+ }
+ r.Schema["client_certificate"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ Description: "The client certificate to use when connecting to the server.",
+ }
+ r.Schema["client_key"] = &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ Description: "The client key to use when connecting to the server.",
+ }
+
+ return r
+}
+
+func (t tlsConfig) pack(p *models.EmbeddedContactPoint, data *schema.ResourceData) (interface{}, error) {
+ settings := p.Settings.(map[string]interface{})
+ tls := make(map[string]interface{})
+
+ if v, ok := settings["insecureSkipVerify"]; ok && v != nil {
+ tls["insecure_skip_verify"] = v.(bool)
+ delete(settings, "insecureSkipVerify")
+ }
+ packNotifierStringField(&settings, &tls, "caCertificate", "ca_certificate")
+ packNotifierStringField(&settings, &tls, "clientCertificate", "client_certificate")
+ packNotifierStringField(&settings, &tls, "clientKey", "client_key")
+
+ return tls, nil
+}
+
+func (t tlsConfig) unpack(raw interface{}) map[string]interface{} {
+ json := raw.(map[string]interface{})
+ tls := make(map[string]interface{})
+
+ if v, ok := json["insecure_skip_verify"]; ok && v != nil {
+ tls["insecureSkipVerify"] = v.(bool)
+ }
+ unpackNotifierStringField(&json, &tls, "ca_certificate", "caCertificate")
+ unpackNotifierStringField(&json, &tls, "client_certificate", "clientCertificate")
+ unpackNotifierStringField(&json, &tls, "client_key", "clientKey")
+
+ return tls
+}
+
type oncallNotifier struct {
}
diff --git a/internal/resources/grafana/resource_alerting_contact_point_test.go b/internal/resources/grafana/resource_alerting_contact_point_test.go
index a14c91bf4..88bc94039 100644
--- a/internal/resources/grafana/resource_alerting_contact_point_test.go
+++ b/internal/resources/grafana/resource_alerting_contact_point_test.go
@@ -399,6 +399,40 @@ func TestAccContactPoint_notifiers10_3(t *testing.T) {
})
}
+func TestAccContactPoint_notifiers11_3(t *testing.T) {
+ testutils.CheckOSSTestsEnabled(t, ">=11.3.0")
+
+ var points models.ContactPoints
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
+ // Implicitly tests deletion.
+ CheckDestroy: alertingContactPointCheckExists.destroyed(&points, nil),
+ Steps: []resource.TestStep{
+ // Test creation.
+ {
+ Config: testutils.TestAccExample(t, "resources/grafana_contact_point/_acc_receiver_types_11_3.tf"),
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+ // mqtt
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.broker_url", "tcp://localhost:1883"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.topic", "grafana/alerts"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.client_id", "grafana"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.message_format", "json"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.username", "user"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.password", "password123"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.qos", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.ca_certificate", "ca_cer"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.client_certificate", "client_cert"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.client_key", "client_key"),
+ ),
+ },
+ },
+ })
+}
+
func TestAccContactPoint_sensitiveData(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")