diff --git a/docs/resources/contact_point.md b/docs/resources/contact_point.md
index e0890e806..16b674c18 100644
--- a/docs/resources/contact_point.md
+++ b/docs/resources/contact_point.md
@@ -523,9 +523,12 @@ Optional:
- `basic_auth_password` (String, Sensitive) The username to use in basic auth headers attached to the request. If omitted, basic auth will not be used.
- `basic_auth_user` (String) The username to use in basic auth headers attached to the request. If omitted, basic auth will not be used.
- `disable_resolve_message` (Boolean) Whether to disable sending resolve messages. Defaults to `false`.
+- `headers` (Map of String) Custom headers to attach to the request.
+- `http_config` (Block Set, Max: 1) Common HTTP client options. (see [below for nested schema](#nestedblock--webhook--http_config))
- `http_method` (String) The HTTP method to use in the request. Defaults to `POST`.
- `max_alerts` (Number) The maximum number of alerts to send in a single request. This can be helpful in limiting the size of the request body. The default is 0, which indicates no limit.
- `message` (String) Custom message. You can use template variables.
+- `payload` (Block Set, Max: 1) Optionally provide a templated payload. Overrides 'Message' and 'Title' field. (see [below for nested schema](#nestedblock--webhook--payload))
- `settings` (Map of String, Sensitive) Additional custom properties to attach to the notifier. Defaults to `map[]`.
- `title` (String) Templated title of the message.
- `tls_config` (Map of String, Sensitive) Allows configuring TLS for the webhook notifier.
@@ -534,6 +537,65 @@ Read-Only:
- `uid` (String) The UID of the contact point.
+
+### Nested Schema for `webhook.http_config`
+
+Optional:
+
+- `oauth2` (Block Set, Max: 1) OAuth2 configuration options. (see [below for nested schema](#nestedblock--webhook--http_config--oauth2))
+
+
+### Nested Schema for `webhook.http_config.oauth2`
+
+Required:
+
+- `client_id` (String) Client ID to use when authenticating.
+- `client_secret` (String, Sensitive) Client secret to use when authenticating.
+- `token_url` (String) URL for the access token endpoint.
+
+Optional:
+
+- `endpoint_params` (Map of String) Optional parameters to append to the access token request.
+- `proxy_config` (Block Set, Max: 1) Optional proxy configuration for OAuth2 requests. (see [below for nested schema](#nestedblock--webhook--http_config--oauth2--proxy_config))
+- `scopes` (List of String) Optional scopes to request when obtaining an access token.
+- `tls_config` (Block Set, Max: 1) Optional TLS configuration options for OAuth2 requests. (see [below for nested schema](#nestedblock--webhook--http_config--oauth2--tls_config))
+
+
+### Nested Schema for `webhook.http_config.oauth2.proxy_config`
+
+Optional:
+
+- `no_proxy` (String) Comma-separated list of addresses that should not use a proxy.
+- `proxy_connect_header` (Map of String) Optional headers to send to proxies during CONNECT requests.
+- `proxy_from_environment` (Boolean) Use environment HTTP_PROXY, HTTPS_PROXY and NO_PROXY to determine proxies. Defaults to `false`.
+- `proxy_url` (String) HTTP proxy server to use to connect to the targets.
+
+
+
+### Nested Schema for `webhook.http_config.oauth2.tls_config`
+
+Optional:
+
+- `ca_certificate` (String, Sensitive) Certificate in PEM format to use when verifying the server's certificate chain.
+- `client_certificate` (String, Sensitive) Client certificate in PEM format to use when connecting to the server.
+- `client_key` (String, Sensitive) Client key in PEM format to use when connecting to the server.
+- `insecure_skip_verify` (Boolean) Do not verify the server's certificate chain and host name. Defaults to `false`.
+
+
+
+
+
+### Nested Schema for `webhook.payload`
+
+Required:
+
+- `template` (String) Custom payload template.
+
+Optional:
+
+- `vars` (Map of String) Optionally provide a variables to be used in the payload template. They will be available in the template as `.Vars.`.
+
+
### Nested Schema for `wecom`
diff --git a/examples/resources/grafana_contact_point/_acc_receiver_types_12_0.tf b/examples/resources/grafana_contact_point/_acc_receiver_types_12_0.tf
new file mode 100644
index 000000000..3279c0292
--- /dev/null
+++ b/examples/resources/grafana_contact_point/_acc_receiver_types_12_0.tf
@@ -0,0 +1,17 @@
+resource "grafana_contact_point" "receiver_types" {
+ name = "Receiver Types since v12.0"
+
+ webhook {
+ url = "http://my-url"
+ headers = {
+ Content-Type = "test-content-type"
+ X-Test-Header = "test-header-value"
+ }
+ payload {
+ template = "{{ .Receiver }}: {{ .Vars.var1 }}"
+ vars = {
+ var1 = "variable value"
+ }
+ }
+ }
+}
diff --git a/examples/resources/grafana_contact_point/_acc_receiver_types_12_1.tf b/examples/resources/grafana_contact_point/_acc_receiver_types_12_1.tf
new file mode 100644
index 000000000..1446528d9
--- /dev/null
+++ b/examples/resources/grafana_contact_point/_acc_receiver_types_12_1.tf
@@ -0,0 +1,70 @@
+resource "grafana_contact_point" "receiver_types" {
+ name = "Receiver Types since v12.1"
+
+ webhook {
+ url = "http://my-url"
+ http_method = "POST"
+ basic_auth_user = "user"
+ basic_auth_password = "password"
+ max_alerts = 100
+ message = "Custom message"
+ title = "Custom title"
+ tls_config = {
+ insecure_skip_verify = true
+ ca_certificate = "ca.crt"
+ client_certificate = "client.crt"
+ client_key = "client.key"
+ }
+ http_config {
+ oauth2 {
+ client_id = "client_id"
+ client_secret = "client_secret"
+ token_url = "http://oauth2-token-url"
+ scopes = ["scope1", "scope2"]
+ endpoint_params = {
+ "param1" = "value1"
+ "param2" = "value2"
+ }
+ proxy_config {
+ proxy_url = "http://proxy-url"
+ proxy_from_environment = false
+ no_proxy = "localhost"
+ proxy_connect_header = {
+ "X-Proxy-Header" = "proxy-value"
+ }
+ }
+ tls_config {
+ insecure_skip_verify = true
+ ca_certificate = < 1 {
+ return map[string]any{}, fmt.Errorf("set contains more than one item: %q", items)
+ }
+ // Use the first item in the set as the child map
+ m, ok := items[0].(map[string]any)
+ if !ok {
+ return map[string]any{}, fmt.Errorf("unsupported value in set: %q", items[0])
+ }
+ return m, nil
}
type notifier interface {
diff --git a/internal/resources/grafana/resource_alerting_contact_point_notifiers.go b/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
index 76939569f..30f469e9e 100644
--- a/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
+++ b/internal/resources/grafana/resource_alerting_contact_point_notifiers.go
@@ -1132,13 +1132,14 @@ func (w webhookNotifier) meta() notifierMeta {
field: "webhook",
typeStr: "webhook",
desc: "A contact point that sends notifications to an arbitrary webhook, using the Prometheus webhook format defined here: https://prometheus.io/docs/alerting/latest/configuration/#webhook_config",
- fieldMapper: map[string]fieldMapper{
+ fieldMapper: withCommonHTTPConfigFieldMappers(map[string]fieldMapper{
"http_method": newKeyMapper("httpMethod"),
"basic_auth_user": newKeyMapper("username"),
"basic_auth_password": newKeyMapper("password"),
"max_alerts": newFieldMapper("maxAlerts", valueAsInt, valueAsInt),
"tls_config": newFieldMapper("tlsConfig", translateTLSConfigPack, translateTLSConfigUnpack),
- },
+ "headers": omitEmptyMapper(),
+ }),
}
}
@@ -1197,6 +1198,34 @@ func (w webhookNotifier) schema() *schema.Resource {
Sensitive: true,
Description: "Allows configuring TLS for the webhook notifier.",
}
+ r.Schema["headers"] = &schema.Schema{
+ Type: schema.TypeMap,
+ Optional: true,
+ Description: "Custom headers to attach to the request.",
+ Elem: &schema.Schema{Type: schema.TypeString},
+ }
+ r.Schema["payload"] = &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ MaxItems: 1,
+ Description: "Optionally provide a templated payload. Overrides 'Message' and 'Title' field.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "template": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Custom payload template.",
+ },
+ "vars": {
+ Type: schema.TypeMap,
+ Optional: true,
+ Description: "Optionally provide a variables to be used in the payload template. They will be available in the template as `.Vars.`.",
+ Elem: &schema.Schema{Type: schema.TypeString},
+ },
+ },
+ },
+ }
+ addCommonHTTPConfigResource(r)
return r
}
diff --git a/internal/resources/grafana/resource_alerting_contact_point_test.go b/internal/resources/grafana/resource_alerting_contact_point_test.go
index c3e38329c..741c69c21 100644
--- a/internal/resources/grafana/resource_alerting_contact_point_test.go
+++ b/internal/resources/grafana/resource_alerting_contact_point_test.go
@@ -529,6 +529,392 @@ func TestAccContactPoint_notifiers11_4(t *testing.T) {
})
}
+func TestAccContactPoint_notifiers12_0(t *testing.T) {
+ testutils.CheckOSSTestsEnabled(t, ">=12.0.0")
+
+ var points models.ContactPoints
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
+ // Implicitly tests deletion.
+ CheckDestroy: alertingContactPointCheckExists.destroyed(&points, nil),
+ Steps: []resource.TestStep{
+ // Multiple payload blocks are not allowed.
+ {
+ Config: `
+ resource "grafana_contact_point" "receiver_types" {
+ name = "Receiver Types since v12.0"
+
+ webhook {
+ url = "http://my-url"
+ headers = {
+ Content-Type = "test-content-type"
+ X-Test-Header = "test-header-value"
+ }
+ payload {
+ template = "{{ .Receiver }}: {{ .Vars.var1 }}"
+ vars = {
+ var1 = "variable value"
+ }
+ }
+ payload {
+ template = "{{ .Receiver }}: {{ .Vars.var1 }} 2"
+ vars = {
+ var1 = "variable value2"
+ }
+ }
+ }
+ }
+ `,
+ ExpectError: regexp.MustCompile(`Too many payload blocks`),
+ },
+ // Template field required.
+ {
+ Config: testutils.TestAccExampleWithReplace(t, "resources/grafana_contact_point/_acc_receiver_types_12_0.tf", map[string]string{
+ `template = "{{ .Receiver }}: {{ .Vars.var1 }}"`: ``,
+ }),
+ ExpectError: regexp.MustCompile(`Missing required argument`),
+ },
+ // Empty payload and header are omitted.
+ {
+ Config: `
+ resource "grafana_contact_point" "receiver_types" {
+ name = "Receiver Types since v12.0"
+
+ webhook {
+ url = "http://my-url"
+ }
+ }
+ `,
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+ // webhook
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+
+ // Sanity check to ensure empty TLS config is omitted instead of being set to an empty map.
+ func(s *terraform.State) error {
+ if val, ok := points[0].Settings.(map[string]interface{})["headers"]; ok {
+ return fmt.Errorf("headers was still present in the settings when it should have been omitted. value: %#v", val)
+ }
+ if val, ok := points[0].Settings.(map[string]interface{})["payload"]; ok {
+ return fmt.Errorf("payload was still present in the settings when it should have been omitted. value: %#v", val)
+ }
+
+ return nil
+ },
+ ),
+ },
+ // Test creation.
+ {
+ Config: testutils.TestAccExample(t, "resources/grafana_contact_point/_acc_receiver_types_12_0.tf"),
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+ // webhook
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+
+ // Since we are using schema.TypeSet for nested types, we need ".0" for this notation to correctly reference the nested element.
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.%", "2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.Content-Type", "test-content-type"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.X-Test-Header", "test-header-value"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.template", "{{ .Receiver }}: {{ .Vars.var1 }}"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.vars.%", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.vars.var1", "variable value"),
+
+ // Sanity check to ensure empty TLS config is omitted instead of being set to an empty map.
+ func(s *terraform.State) error {
+ if val, ok := points[0].Settings.(map[string]interface{})["tlsConfig"]; ok {
+ return fmt.Errorf("tlsConfig was still present in the settings when it should have been omitted. value: %#v", val)
+ }
+
+ return nil
+ },
+ ),
+ },
+ // Update non-sensitive data.
+ {
+ Config: testutils.TestAccExampleWithReplace(t, "resources/grafana_contact_point/_acc_receiver_types_12_0.tf", map[string]string{
+ "\"test-header-value\"": "\"updated-test-header-value\"",
+ "\"variable value\"": "\"updated-variable value\"",
+ "\"{{ .Receiver }}: {{ .Vars.var1 }}\"": "\"Updated {{ .Receiver }}: {{ .Vars.var1 }}\"",
+ }),
+
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+ // webhook
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+
+ // Since we are using schema.TypeSet for nested types, we need ".0" for this notation to correctly reference the nested element.
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.%", "2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.Content-Type", "test-content-type"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.headers.X-Test-Header", "updated-test-header-value"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.template", "Updated {{ .Receiver }}: {{ .Vars.var1 }}"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.vars.%", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.payload.0.vars.var1", "updated-variable value"),
+
+ // Sanity check to ensure empty TLS config is omitted instead of being set to an empty map.
+ func(s *terraform.State) error {
+ if val, ok := points[0].Settings.(map[string]interface{})["tlsConfig"]; ok {
+ return fmt.Errorf("tlsConfig was still present in the settings when it should have been omitted. value: %#v", val)
+ }
+
+ return nil
+ },
+ ),
+ },
+ },
+ })
+}
+
+func TestAccContactPoint_notifiers12_1(t *testing.T) {
+ testutils.CheckOSSTestsEnabled(t, ">=12.1.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_12_1.tf"),
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+ // webhook
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_method", "POST"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_user", "user"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_password", "password"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.max_alerts", "100"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.message", "Custom message"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.title", "Custom title"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.ca_certificate", "ca.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_certificate", "client.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_key", "client.key"),
+
+ // Since we are using schema.TypeSet for nested types, we need ".0" for this notation to correctly reference the nested element.
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_id", "client_id"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_secret", "client_secret"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.token_url", "http://oauth2-token-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.#", "2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.0", "scope1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.1", "scope2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param1", "value1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param2", "value2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_url", "http://proxy-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_from_environment", "false"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.no_proxy", "localhost"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_connect_header.X-Proxy-Header", "proxy-value"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.ca_certificate", "-----BEGIN CERTIFICATE-----\nMIGrMF+gAwIBAgIBATAFBgMrZXAwADAeFw0yNDExMTYxMDI4MzNaFw0yNTExMTYx\nMDI4MzNaMAAwKjAFBgMrZXADIQCf30GvRnHbs9gukA3DLXDK6W5JVgYw6mERU/60\n2M8+rjAFBgMrZXADQQCGmeaRp/AcjeqmJrF5Yh4d7aqsMSqVZvfGNDc0ppXyUgS3\nWMQ1+3T+/pkhU612HR0vFd3vyFhmB4yqFoNV8RML\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_certificate", "-----BEGIN CERTIFICATE-----\nMIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw\nDgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow\nEjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d\n7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B\n5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr\nBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1\nNDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l\nWf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc\n6MF9+Yw1Yy0t\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_key", "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49\nAwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q\nEKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==\n-----END EC PRIVATE KEY-----\n"),
+ ),
+ },
+ // Update sensitive data.
+ {
+ Config: testutils.TestAccExampleWithReplace(t, "resources/grafana_contact_point/_acc_receiver_types_12_1.tf", map[string]string{
+ "\"client_secret\"": "\"updated_client_secret\"",
+ }),
+
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+
+ // Updated
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_secret", "updated_client_secret"),
+
+ // Unchanged
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_method", "POST"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_user", "user"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_password", "password"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.max_alerts", "100"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.message", "Custom message"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.title", "Custom title"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.ca_certificate", "ca.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_certificate", "client.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_key", "client.key"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_id", "client_id"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.token_url", "http://oauth2-token-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.#", "2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.0", "scope1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.1", "scope2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param1", "value1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param2", "value2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_url", "http://proxy-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_from_environment", "false"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.no_proxy", "localhost"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_connect_header.X-Proxy-Header", "proxy-value"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.ca_certificate", "-----BEGIN CERTIFICATE-----\nMIGrMF+gAwIBAgIBATAFBgMrZXAwADAeFw0yNDExMTYxMDI4MzNaFw0yNTExMTYx\nMDI4MzNaMAAwKjAFBgMrZXADIQCf30GvRnHbs9gukA3DLXDK6W5JVgYw6mERU/60\n2M8+rjAFBgMrZXADQQCGmeaRp/AcjeqmJrF5Yh4d7aqsMSqVZvfGNDc0ppXyUgS3\nWMQ1+3T+/pkhU612HR0vFd3vyFhmB4yqFoNV8RML\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_certificate", "-----BEGIN CERTIFICATE-----\nMIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw\nDgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow\nEjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d\n7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B\n5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr\nBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1\nNDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l\nWf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc\n6MF9+Yw1Yy0t\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_key", "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49\nAwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q\nEKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==\n-----END EC PRIVATE KEY-----\n"),
+ ),
+ },
+ // Update non-sensitive data.
+ {
+ Config: testutils.TestAccExampleWithReplace(t, "resources/grafana_contact_point/_acc_receiver_types_12_1.tf", map[string]string{
+ "\"client_secret\"": "\"updated_client_secret\"",
+ "http://proxy-url": "http://updated-proxy-url",
+ }),
+
+ Check: resource.ComposeTestCheckFunc(
+ checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
+
+ // Updated
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_secret", "updated_client_secret"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_url", "http://updated-proxy-url"),
+
+ // Unchanged
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.#", "1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.url", "http://my-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_method", "POST"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_user", "user"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.basic_auth_password", "password"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.max_alerts", "100"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.message", "Custom message"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.title", "Custom title"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.ca_certificate", "ca.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_certificate", "client.crt"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.tls_config.client_key", "client.key"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.client_id", "client_id"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.token_url", "http://oauth2-token-url"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.#", "2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.0", "scope1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.scopes.1", "scope2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param1", "value1"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.endpoint_params.param2", "value2"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_from_environment", "false"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.no_proxy", "localhost"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.proxy_config.0.proxy_connect_header.X-Proxy-Header", "proxy-value"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.insecure_skip_verify", "true"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.ca_certificate", "-----BEGIN CERTIFICATE-----\nMIGrMF+gAwIBAgIBATAFBgMrZXAwADAeFw0yNDExMTYxMDI4MzNaFw0yNTExMTYx\nMDI4MzNaMAAwKjAFBgMrZXADIQCf30GvRnHbs9gukA3DLXDK6W5JVgYw6mERU/60\n2M8+rjAFBgMrZXADQQCGmeaRp/AcjeqmJrF5Yh4d7aqsMSqVZvfGNDc0ppXyUgS3\nWMQ1+3T+/pkhU612HR0vFd3vyFhmB4yqFoNV8RML\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_certificate", "-----BEGIN CERTIFICATE-----\nMIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw\nDgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow\nEjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d\n7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B\n5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr\nBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1\nNDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l\nWf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc\n6MF9+Yw1Yy0t\n-----END CERTIFICATE-----\n"),
+ resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "webhook.0.http_config.0.oauth2.0.tls_config.0.client_key", "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49\nAwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q\nEKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==\n-----END EC PRIVATE KEY-----\n"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccContactPoint_TypeSet_MaxItems(t *testing.T) {
+ testutils.CheckOSSTestsEnabled(t, ">=12.1.0")
+
+ resource.ParallelTest(t, resource.TestCase{
+ ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
+ Steps: []resource.TestStep{
+ // Multiple http_config blocks are not allowed.
+ {
+ Config: `
+ resource "grafana_contact_point" "typset_max_items" {
+ name = "typset_max_items"
+ webhook {
+ url = "http://my-url"
+ http_config {
+ oauth2 {
+ client_id = "client_id"
+ client_secret = "client_secret"
+ token_url = "http://oauth2-token-url"
+ }
+ }
+ http_config {
+ oauth2 {
+ client_id = "client_id2"
+ client_secret = "client_secret2"
+ token_url = "http://oauth2-token-url2"
+ }
+ }
+ }
+ }
+ `,
+ ExpectError: regexp.MustCompile(`Too many http_config blocks`),
+ },
+ // Multiple oauth2 blocks are not allowed.
+ {
+ Config: `
+ resource "grafana_contact_point" "typset_max_items" {
+ name = "typset_max_items"
+ webhook {
+ url = "http://my-url"
+ http_config {
+ oauth2 {
+ client_id = "client_id"
+ client_secret = "client_secret"
+ token_url = "http://oauth2-token-url"
+ }
+ oauth2 {
+ client_id = "client_id2"
+ client_secret = "client_secret2"
+ token_url = "http://oauth2-token-url2"
+ }
+ }
+ }
+ }
+ `,
+ ExpectError: regexp.MustCompile(`Too many oauth2 blocks`),
+ },
+ // Multiple proxy_config blocks are not allowed.
+ {
+ Config: `
+ resource "grafana_contact_point" "typset_max_items" {
+ name = "typset_max_items"
+ webhook {
+ url = "http://my-url"
+ http_config {
+ oauth2 {
+ client_id = "client_id"
+ client_secret = "client_secret"
+ token_url = "http://oauth2-token-url"
+ proxy_config {
+ proxy_url = "http://proxy-url"
+ }
+ proxy_config {
+ proxy_url = "http://proxy-url"
+ }
+ }
+ }
+ }
+ }
+ `,
+ ExpectError: regexp.MustCompile(`Too many proxy_config blocks`),
+ },
+ // Multiple tls_config blocks are not allowed.
+ {
+ Config: `
+ resource "grafana_contact_point" "typset_max_items" {
+ name = "typset_max_items"
+ webhook {
+ url = "http://my-url"
+ http_config {
+ oauth2 {
+ client_id = "client_id"
+ client_secret = "client_secret"
+ token_url = "http://oauth2-token-url"
+ tls_config {
+ insecure_skip_verify = true
+ }
+ tls_config {
+ insecure_skip_verify = true
+ }
+ }
+ }
+ }
+ }
+ `,
+ ExpectError: regexp.MustCompile(`Too many tls_config blocks`),
+ },
+ },
+ })
+}
+
func TestAccContactPoint_sensitiveData(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")
@@ -766,8 +1152,8 @@ func TestAccContactPoint_minimalDefinitions(t *testing.T) {
expectedAttrs[attr] = struct{}{}
}
for attr := range pt.Settings.(map[string]any) {
- if _, ok := expectedAttrs[attr]; !ok {
- return fmt.Errorf("contact point %s attribute %s should not exist in the contact point settings, but was found", key, attr)
+ if val, ok := expectedAttrs[attr]; !ok {
+ return fmt.Errorf("contact point %s attribute %s should not exist in the contact point settings, but was found: %v", key, attr, val)
}
delete(expectedAttrs, attr)
}
@@ -1038,6 +1424,9 @@ func TestAccContactPoint_minimalDefinitions(t *testing.T) {
resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.max_alerts", "0"),
resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.message", ""),
resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.title", ""),
+ resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.headers.%", "0"),
+ resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.payload.#", "0"),
+ resource.TestCheckResourceAttr("grafana_contact_point.minimal_receivers", "webhook.0.http_config.#", "0"),
checkOtherAttrsOmittedInResponse(&points, "webhook.0",
"url",
"maxAlerts", // TODO: This would be better omitted.