diff --git a/internal/services/signalr/signalr_service_resource.go b/internal/services/signalr/signalr_service_resource.go index 8d87bfe98472..63481a876dcf 100644 --- a/internal/services/signalr/signalr_service_resource.go +++ b/internal/services/signalr/signalr_service_resource.go @@ -3,6 +3,7 @@ package signalr import ( "fmt" "log" + "strconv" "strings" "time" @@ -82,10 +83,15 @@ func resourceArmSignalRService() *pluginsdk.Resource { }, }, + // TODO: Remove in 3.0 "features": { - Type: pluginsdk.TypeSet, - Optional: true, - Computed: true, + Type: pluginsdk.TypeSet, + Optional: true, + Computed: true, + Deprecated: "Deprecated in favour of `connectivity_logs_enabled`, `messaging_logs_enabled` and `service_mode`", + ConflictsWith: []string{ + "connectivity_logs_enabled", "messaging_logs_enabled", "service_mode", + }, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "flag": { @@ -106,6 +112,38 @@ func resourceArmSignalRService() *pluginsdk.Resource { }, }, + "connectivity_logs_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Computed: true, // TODO remove in 3.0 + ConflictsWith: []string{ + "features", + }, + }, + + "messaging_logs_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Computed: true, // TODO remove in 3.0 + ConflictsWith: []string{ + "features", + }, + }, + + "service_mode": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, // TODO remove in 3.0 + ConflictsWith: []string{ + "features", + }, + ValidateFunc: validation.StringInSlice([]string{ + "Serverless", + "Classic", + "Default", + }, false), + }, + "upstream_endpoint": { Type: pluginsdk.TypeSet, Optional: true, @@ -235,13 +273,24 @@ func resourceArmSignalRServiceCreate(d *pluginsdk.ResourceData, meta interface{} sku := d.Get("sku").([]interface{}) featureFlags := d.Get("features").(*pluginsdk.Set).List() + connectivityLogsEnabled := d.Get("connectivity_logs_enabled").(bool) + messagingLogsEnabled := d.Get("messaging_logs_enabled").(bool) + serviceMode := d.Get("service_mode").(string) + cors := d.Get("cors").([]interface{}) upstreamSettings := d.Get("upstream_endpoint").(*pluginsdk.Set).List() - expandedFeatures := expandSignalRFeatures(featureFlags) + expandedFeatures := make([]signalr.SignalRFeature, 0) + if len(featureFlags) > 0 { + expandedFeatures = *expandSignalRFeatures(featureFlags) + } else { + expandedFeatures = append(expandedFeatures, signalRFeature(signalr.FeatureFlagsEnableConnectivityLogs, strconv.FormatBool(connectivityLogsEnabled))) + expandedFeatures = append(expandedFeatures, signalRFeature(signalr.FeatureFlagsEnableMessagingLogs, strconv.FormatBool(messagingLogsEnabled))) + expandedFeatures = append(expandedFeatures, signalRFeature(signalr.FeatureFlagsServiceMode, serviceMode)) + } // Upstream configurations are only allowed when the SignalR service is in `Serverless` mode - if len(upstreamSettings) > 0 && !signalRIsInServerlessMode(expandedFeatures) { + if len(upstreamSettings) > 0 && !signalRIsInServerlessMode(&expandedFeatures) { return fmt.Errorf("Upstream configurations are only allowed when the SignalR Service is in `Serverless` mode") } @@ -249,7 +298,7 @@ func resourceArmSignalRServiceCreate(d *pluginsdk.ResourceData, meta interface{} Location: utils.String(location), Properties: &signalr.SignalRProperties{ Cors: expandSignalRCors(cors), - Features: expandedFeatures, + Features: &expandedFeatures, Upstream: expandUpstreamSettings(upstreamSettings), }, Sku: expandSignalRServiceSku(sku), @@ -310,6 +359,24 @@ func resourceArmSignalRServiceRead(d *pluginsdk.ResourceData, meta interface{}) return fmt.Errorf("setting `features`: %+v", err) } + connectivityLogsEnabled := false + messagingLogsEnabled := false + serviceMode := "Default" + for _, feature := range *props.Features { + if feature.Flag == signalr.FeatureFlagsEnableConnectivityLogs { + connectivityLogsEnabled = strings.EqualFold(feature.Value, "True") + } + if feature.Flag == signalr.FeatureFlagsEnableMessagingLogs { + messagingLogsEnabled = strings.EqualFold(feature.Value, "True") + } + if feature.Flag == signalr.FeatureFlagsServiceMode { + serviceMode = feature.Value + } + } + d.Set("connectivity_logs_enabled", connectivityLogsEnabled) + d.Set("messaging_logs_enabled", messagingLogsEnabled) + d.Set("service_mode", serviceMode) + if err := d.Set("cors", flattenSignalRCors(props.Cors)); err != nil { return fmt.Errorf("setting `cors`: %+v", err) } @@ -346,7 +413,7 @@ func resourceArmSignalRServiceUpdate(d *pluginsdk.ResourceData, meta interface{} resourceType := signalr.SignalRResource{} - if d.HasChanges("cors", "features", "upstream_endpoint") { + if d.HasChanges("cors", "features", "upstream_endpoint", "connectivity_logs_enabled", "messaging_logs_enabled", "service_mode") { resourceType.Properties = &signalr.SignalRProperties{} if d.HasChange("cors") { @@ -359,6 +426,25 @@ func resourceArmSignalRServiceUpdate(d *pluginsdk.ResourceData, meta interface{} resourceType.Properties.Features = expandSignalRFeatures(featuresRaw) } + if d.HasChanges("connectivity_logs_enabled", "messaging_logs_enabled", "service_mode") { + features := make([]signalr.SignalRFeature, 0) + if d.HasChange("connectivity_logs_enabled") { + connectivityLogsEnabled := d.Get("connectivity_logs_enabled").(bool) + features = append(features, signalRFeature(signalr.FeatureFlagsEnableConnectivityLogs, strconv.FormatBool(connectivityLogsEnabled))) + } + + if d.HasChange("messaging_logs_enabled") { + messagingLogsEnabled := d.Get("messaging_logs_enabled").(bool) + features = append(features, signalRFeature(signalr.FeatureFlagsEnableMessagingLogs, strconv.FormatBool(messagingLogsEnabled))) + } + + if d.HasChange("service_mode") { + serviceMode := d.Get("service_mode").(string) + features = append(features, signalRFeature(signalr.FeatureFlagsServiceMode, serviceMode)) + } + resourceType.Properties.Features = &features + } + if d.HasChange("upstream_endpoint") { featuresRaw := d.Get("upstream_endpoint").(*pluginsdk.Set).List() resourceType.Properties.Upstream = expandUpstreamSettings(featuresRaw) @@ -425,18 +511,18 @@ func expandSignalRFeatures(input []interface{}) *[]signalr.SignalRFeature { features := make([]signalr.SignalRFeature, 0) for _, featureValue := range input { value := featureValue.(map[string]interface{}) - - feature := signalr.SignalRFeature{ - Flag: signalr.FeatureFlags(value["flag"].(string)), - Value: value["value"].(string), - } - - features = append(features, feature) + features = append(features, signalRFeature(signalr.FeatureFlags(value["flag"].(string)), value["value"].(string))) } - return &features } +func signalRFeature(featureFlag signalr.FeatureFlags, value string) signalr.SignalRFeature { + return signalr.SignalRFeature{ + Flag: featureFlag, + Value: value, + } +} + func flattenSignalRFeatures(features *[]signalr.SignalRFeature) []interface{} { if features == nil { return []interface{}{} diff --git a/internal/services/signalr/signalr_service_resource_test.go b/internal/services/signalr/signalr_service_resource_test.go index 13b89278f205..3835d5f73575 100644 --- a/internal/services/signalr/signalr_service_resource_test.go +++ b/internal/services/signalr/signalr_service_resource_test.go @@ -308,6 +308,49 @@ func TestAccSignalRService_serviceMode(t *testing.T) { }) } +func TestAccSignalRService_featureFlags(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_signalr_service", "test") + r := SignalRServiceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withFeatureFlags(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("hostname").Exists(), + check.That(data.ResourceName).Key("ip_address").Exists(), + check.That(data.ResourceName).Key("public_port").Exists(), + check.That(data.ResourceName).Key("server_port").Exists(), + check.That(data.ResourceName).Key("primary_access_key").Exists(), + check.That(data.ResourceName).Key("primary_connection_string").Exists(), + check.That(data.ResourceName).Key("secondary_access_key").Exists(), + check.That(data.ResourceName).Key("secondary_connection_string").Exists(), + check.That(data.ResourceName).Key("connectivity_logs_enabled").HasValue("true"), + check.That(data.ResourceName).Key("messaging_logs_enabled").HasValue("true"), + check.That(data.ResourceName).Key("service_mode").HasValue("Default"), + ), + }, + data.ImportStep(), + { + Config: r.withFeatureFlagsUpdated(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("hostname").Exists(), + check.That(data.ResourceName).Key("ip_address").Exists(), + check.That(data.ResourceName).Key("public_port").Exists(), + check.That(data.ResourceName).Key("server_port").Exists(), + check.That(data.ResourceName).Key("primary_access_key").Exists(), + check.That(data.ResourceName).Key("primary_connection_string").Exists(), + check.That(data.ResourceName).Key("secondary_access_key").Exists(), + check.That(data.ResourceName).Key("secondary_connection_string").Exists(), + check.That(data.ResourceName).Key("connectivity_logs_enabled").HasValue("false"), + check.That(data.ResourceName).Key("messaging_logs_enabled").HasValue("false"), + check.That(data.ResourceName).Key("service_mode").HasValue("Classic"), + ), + }, + }) +} + func TestAccSignalRService_cors(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_signalr_service", "test") r := SignalRServiceResource{} @@ -565,3 +608,61 @@ resource "azurerm_signalr_service" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } + +func (r SignalRServiceResource) withFeatureFlags(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_signalr_service" "test" { + name = "acctestSignalR-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku { + name = "Free_F1" + capacity = 1 + } + + connectivity_logs_enabled = true + messaging_logs_enabled = true + service_mode = "Default" + +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (r SignalRServiceResource) withFeatureFlagsUpdated(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_signalr_service" "test" { + name = "acctestSignalR-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku { + name = "Standard_S1" + capacity = 1 + } + + connectivity_logs_enabled = false + messaging_logs_enabled = false + service_mode = "Classic" + +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/website/docs/r/signalr_service.html.markdown b/website/docs/r/signalr_service.html.markdown index 6191363debda..9bf5bd6577ea 100644 --- a/website/docs/r/signalr_service.html.markdown +++ b/website/docs/r/signalr_service.html.markdown @@ -32,10 +32,9 @@ resource "azurerm_signalr_service" "example" { allowed_origins = ["http://www.example.com"] } - features { - flag = "ServiceMode" - value = "Default" - } + connectivity_logs_enabled = "True" + messaging_logs_enabled = "True" + service_mode = "Default" upstream_endpoint { category_pattern = ["connections", "messages"] @@ -62,6 +61,14 @@ The following arguments are supported: * `features` - (Optional) A `features` block as documented below. +~> **NOTE:** The `features` block is deprecated, use `connectivity_logs_enabled`, `messaging_logs_enabled` and `service_mode` instead. + +* `connectivity_logs_enabled`- (Optional) Specifies if Connectivity Logs are enabled or not. + +* `messaging_logs_enabled`- (Optional) Specifies if Messaging Logs are enabled or not. + +* `service_mode`- (Optional) Specifies the service mode. Possible values are `Classic`, `Default` and `Serverless`. + * `upstream_endpoint` - (Optional) An `upstream_endpoint` block as documented below. Using this block requires the SignalR service to be Serverless. When creating multiple blocks they will be processed in the order they are defined in. * `tags` - (Optional) A mapping of tags to assign to the resource.