diff --git a/internal/services/dashboard/dashboard_grafana_resource.go b/internal/services/dashboard/dashboard_grafana_resource.go index f99758f8c823..1ac4119b48cb 100644 --- a/internal/services/dashboard/dashboard_grafana_resource.go +++ b/internal/services/dashboard/dashboard_grafana_resource.go @@ -9,11 +9,13 @@ import ( "regexp" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2023-09-01/grafanaresource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -25,6 +27,7 @@ type DashboardGrafanaModel struct { ResourceGroupName string `tfschema:"resource_group_name"` ApiKeyEnabled bool `tfschema:"api_key_enabled"` AutoGeneratedDomainNameLabelScope grafanaresource.AutoGeneratedDomainNameLabelScope `tfschema:"auto_generated_domain_name_label_scope"` + SMTP []SMTPConfigurationModel `tfschema:"smtp"` DeterministicOutboundIPEnabled bool `tfschema:"deterministic_outbound_ip_enabled"` AzureMonitorWorkspaceIntegrations []AzureMonitorWorkspaceIntegrationModel `tfschema:"azure_monitor_workspace_integrations"` Location string `tfschema:"location"` @@ -42,6 +45,17 @@ type AzureMonitorWorkspaceIntegrationModel struct { ResourceId string `tfschema:"resource_id"` } +type SMTPConfigurationModel struct { + SMTPEnabled bool `tfschema:"enabled"` + FromAddress string `tfschema:"from_address"` + FromName string `tfschema:"from_name"` + Host string `tfschema:"host"` + Password string `tfschema:"password"` + SkipVerify bool `tfschema:"verification_skip_enabled"` + User string `tfschema:"user"` + StartTLSPolicy string `tfschema:"start_tls_policy"` +} + type DashboardGrafanaResource struct{} var _ sdk.ResourceWithUpdate = DashboardGrafanaResource{} @@ -89,6 +103,61 @@ func (r DashboardGrafanaResource) Arguments() map[string]*pluginsdk.Schema { }, false), }, + "smtp": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "from_address": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "from_name": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "Azure Managed Grafana Notification", + ValidateFunc: validation.StringIsNotEmpty, + }, + "host": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "password": { + Type: pluginsdk.TypeString, + Required: true, + Sensitive: true, + }, + "verification_skip_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "user": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "start_tls_policy": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(grafanaresource.StartTLSPolicyOpportunisticStartTLS), + string(grafanaresource.StartTLSPolicyMandatoryStartTLS), + string(grafanaresource.StartTLSPolicyNoStartTLS), + }, false), + }, + }, + }, + }, + "deterministic_outbound_ip_enabled": { Type: pluginsdk.TypeBool, Optional: true, @@ -221,6 +290,7 @@ func (r DashboardGrafanaResource) Create() sdk.ResourceFunc { Properties: &grafanaresource.ManagedGrafanaProperties{ ApiKey: &apiKey, AutoGeneratedDomainNameLabelScope: &model.AutoGeneratedDomainNameLabelScope, + GrafanaConfigurations: expandSMTPConfigurationModel(model.SMTP), DeterministicOutboundIP: &deterministicOutboundIP, GrafanaIntegrations: expandGrafanaIntegrationsModel(model.AzureMonitorWorkspaceIntegrations), GrafanaMajorVersion: &model.GrafanaMajorVersion, @@ -367,6 +437,10 @@ func (r DashboardGrafanaResource) Read() sdk.ResourceFunc { state.AutoGeneratedDomainNameLabelScope = *properties.AutoGeneratedDomainNameLabelScope } + if properties.GrafanaConfigurations != nil { + state.SMTP = flattenSMTPConfigurationModel(properties.GrafanaConfigurations.Smtp, metadata.ResourceData) + } + if properties.DeterministicOutboundIP != nil { if *properties.DeterministicOutboundIP == grafanaresource.DeterministicOutboundIPEnabled { state.DeterministicOutboundIPEnabled = true @@ -445,6 +519,29 @@ func (r DashboardGrafanaResource) Delete() sdk.ResourceFunc { } } +func expandSMTPConfigurationModel(input []SMTPConfigurationModel) *grafanaresource.GrafanaConfigurations { + if len(input) == 0 { + return nil + } + + v := input[0] + smtp := grafanaresource.Smtp{ + Enabled: pointer.To(v.SMTPEnabled), + FromAddress: pointer.To(v.FromAddress), + FromName: pointer.To(v.FromName), + Host: pointer.To(v.Host), + Password: pointer.To(v.Password), + SkipVerify: pointer.To(v.SkipVerify), + User: pointer.To(v.User), + StartTLSPolicy: (*grafanaresource.StartTLSPolicy)(pointer.To(v.StartTLSPolicy)), + } + + return pointer.To( + grafanaresource.GrafanaConfigurations{ + Smtp: pointer.To(smtp), + }) +} + func expandGrafanaIntegrationsModel(inputList []AzureMonitorWorkspaceIntegrationModel) *grafanaresource.GrafanaIntegrations { if len(inputList) == 0 { return nil @@ -483,6 +580,49 @@ func expandLegacySystemAndUserAssignedMap(input []interface{}) *identity.LegacyS } } +func flattenSMTPConfigurationModel(input *grafanaresource.Smtp, data *schema.ResourceData) []SMTPConfigurationModel { + var outputList []SMTPConfigurationModel + if input == nil || !pointer.From(input.Enabled) { + return outputList + } + + var output SMTPConfigurationModel + + if input.Enabled != nil { + output.SMTPEnabled = pointer.From(input.Enabled) + } + + if input.FromAddress != nil { + output.FromAddress = pointer.From(input.FromAddress) + } + + if input.FromName != nil { + output.FromName = pointer.From(input.FromName) + } + + if input.Host != nil { + output.Host = pointer.From(input.Host) + } + + if input.SkipVerify != nil { + output.SkipVerify = pointer.From(input.SkipVerify) + } + + if input.User != nil { + output.User = pointer.From(input.User) + } + + if input.StartTLSPolicy != nil { + output.StartTLSPolicy = string(pointer.From(input.StartTLSPolicy)) + } + + output.Password = data.Get("smtp.0.password").(string) + + outputList = append(outputList, output) + + return outputList +} + func flattenAzureMonitorWorkspaceIntegrationModelArray(inputList *[]grafanaresource.AzureMonitorWorkspaceIntegration) []AzureMonitorWorkspaceIntegrationModel { var outputList []AzureMonitorWorkspaceIntegrationModel if inputList == nil { diff --git a/internal/services/dashboard/dashboard_grafana_resource_test.go b/internal/services/dashboard/dashboard_grafana_resource_test.go index caff513f7d02..9106b8d6e153 100644 --- a/internal/services/dashboard/dashboard_grafana_resource_test.go +++ b/internal/services/dashboard/dashboard_grafana_resource_test.go @@ -57,7 +57,7 @@ func TestAccDashboardGrafana_complete(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("smtp.0.password"), }) } @@ -85,14 +85,14 @@ func TestAccDashboardGrafana_update(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("smtp.0.password"), { Config: r.update(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("smtp.0.password"), }) } @@ -217,6 +217,15 @@ resource "azurerm_dashboard_grafana" "test" { deterministic_outbound_ip_enabled = true public_network_access_enabled = false grafana_major_version = "9" + smtp { + enabled = true + host = "localhost:25" + user = "user" + password = "password" + from_address = "admin@grafana.localhost" + from_name = "Grafana" + start_tls_policy = "OpportunisticStartTLS" + } identity { type = "UserAssigned" diff --git a/website/docs/r/dashboard_grafana.html.markdown b/website/docs/r/dashboard_grafana.html.markdown index 5333563bf0b7..75acdc24dd6a 100644 --- a/website/docs/r/dashboard_grafana.html.markdown +++ b/website/docs/r/dashboard_grafana.html.markdown @@ -54,6 +54,8 @@ The following arguments are supported: * `grafana_major_version` - (Optional) Which major version of Grafana to deploy. Defaults to `9`. Possible values are `9`, `10`. Changing this forces a new resource to be created. +* `smtp` - (Optional) A `smtp` block as defined below. + * `azure_monitor_workspace_integrations` - (Optional) A `azure_monitor_workspace_integrations` block as defined below. * `identity` - (Optional) An `identity` block as defined below. Changing this forces a new Dashboard Grafana to be created. @@ -68,6 +70,26 @@ The following arguments are supported: --- +A `smtp` block supports the following: + +* `enabled` - (Optional) Whether to enable the smtp setting of the Grafana instance. Defaults to `false`. + +* `host` - (Required) SMTP server hostname with port, e.g. test.email.net:587 + +* `user` - (Required) User of SMTP authentication. + +* `password` - (Required) Password of SMTP authentication. +* +* `start_tls_policy` - (Required) Whether to use TLS when connecting to SMTP server. Possible values are `OpportunisticStartTLS`, `NoStartTLS`, `MandatoryStartTLS`. + +* `from_address` - (Required) Address used when sending emails. + +* `from_name` - (Optional) Name used when sending emails. Defaults to `Azure Managed Grafana Notification`. + +* `verification_skip_enabled` - (Optional) Whether verify SSL for SMTP server. Defaults to `false`. + +--- + An `azure_monitor_workspace_integrations` block supports the following: * `resource_id` - (Required) Specifies the resource ID of the connected Azure Monitor Workspace.