From dd6e374f803249499e19d4c04425bc10c03cf419 Mon Sep 17 00:00:00 2001 From: Jerome Brown Date: Wed, 22 May 2024 12:08:55 +1200 Subject: [PATCH 1/8] `azurerm_container_app_envrionment` - add support for Azure Monitor as a log destination --- .../container_app_environment_resource.go | 27 +++++++++++++++++++ ...container_app_environment_resource_test.go | 23 ++++++++++++---- .../r/container_app_environment.html.markdown | 7 ++++- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index d5f2a246cc5e..d7da0564402b 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -32,6 +32,7 @@ type ContainerAppEnvironmentModel struct { Location string `tfschema:"location"` DaprApplicationInsightsConnectionString string `tfschema:"dapr_application_insights_connection_string"` LogAnalyticsWorkspaceId string `tfschema:"log_analytics_workspace_id"` + LogsDestination string `tfschema:"logs_destination"` InfrastructureSubnetId string `tfschema:"infrastructure_subnet_id"` InternalLoadBalancerEnabled bool `tfschema:"internal_load_balancer_enabled"` ZoneRedundant bool `tfschema:"zone_redundancy_enabled"` @@ -95,6 +96,14 @@ func (r ContainerAppEnvironmentResource) Arguments() map[string]*pluginsdk.Schem Description: "The ID for the Log Analytics Workspace to link this Container Apps Managed Environment to.", }, + "logs_destination": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{"log-analytics", "azure-monitor"}, false), + Description: "The destination for the application logs. Possible values are `log-analytics` or `azure-monitor`.", + }, + "infrastructure_resource_group_name": { Type: pluginsdk.TypeString, Optional: true, @@ -248,6 +257,12 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { managedEnvironment.Properties.InfrastructureResourceGroup = pointer.To(containerAppEnvironment.InfrastructureResourceGroup) } + if containerAppEnvironment.LogsDestination != "" && containerAppEnvironment.LogsDestination != "log-analytics" { + managedEnvironment.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ + Destination: pointer.To(containerAppEnvironment.LogsDestination), + } + } + if containerAppEnvironment.LogAnalyticsWorkspaceId != "" { logAnalyticsId, err := workspaces.ParseWorkspaceID(containerAppEnvironment.LogAnalyticsWorkspaceId) if err != nil { @@ -337,6 +352,10 @@ func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { state.PlatformReservedDnsIP = pointer.From(vnet.PlatformReservedDnsIP) } + if appLogsConfig := props.AppLogsConfiguration; appLogsConfig != nil { + state.LogsDestination = *appLogsConfig.Destination + } + state.CustomDomainVerificationId = pointer.From(props.CustomDomainConfiguration.CustomDomainVerificationId) state.ZoneRedundant = pointer.From(props.ZoneRedundant) state.StaticIP = pointer.From(props.StaticIP) @@ -511,6 +530,14 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { } } + if env.LogsDestination == "log-analytics" && env.LogAnalyticsWorkspaceId == "" { + return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") + } + + if env.LogAnalyticsWorkspaceId != "" && env.LogsDestination != "log-analytics" { + return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") + } + return nil }, } diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index e97718d18073..f6cd64a9508c 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -246,7 +246,7 @@ resource "azurerm_container_app_environment" "test" { name = "acctest-CAEnv%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + logs_destination = "azure-monitor" infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true @@ -265,6 +265,21 @@ resource "azurerm_container_app_environment" "test" { secret = "sauce" } } + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "diagnostics" + target_resource_id = azurerm_container_app_environment.test.id + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + + enabled_log { + category_group = "allLogs" + } + + metric { + category = "AllMetrics" + enabled = true + } +} `, r.templateVNet(data), data.RandomInteger) } @@ -306,7 +321,6 @@ resource "azurerm_container_app_environment" "test" { name = "acctest-CAEnv%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id workload_profile { name = "Consumption" @@ -336,7 +350,8 @@ resource "azurerm_container_app_environment" "test" { name = "acctest-CAEnv%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - log_analytics_workspace_id = azurerm_log_analytics_workspace.second.id + logs_destination = "log-analytics" + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true @@ -369,7 +384,6 @@ resource "azurerm_container_app_environment" "test" { name = "acctest-CAEnv%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true @@ -416,7 +430,6 @@ resource "azurerm_container_app_environment" "test" { name = "acctest-CAEnv%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id infrastructure_subnet_id = azurerm_subnet.control.id zone_redundancy_enabled = true internal_load_balancer_enabled = true diff --git a/website/docs/r/container_app_environment.html.markdown b/website/docs/r/container_app_environment.html.markdown index 9a878d0cf9d9..b7bb12711c26 100644 --- a/website/docs/r/container_app_environment.html.markdown +++ b/website/docs/r/container_app_environment.html.markdown @@ -30,6 +30,7 @@ resource "azurerm_container_app_environment" "example" { name = "my-environment" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name + logs_destination = "log-analytics" log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id } ``` @@ -64,7 +65,11 @@ The following arguments are supported: ~> **Note:** can only be set to `true` if `infrastructure_subnet_id` is specified. -* `log_analytics_workspace_id` - (Optional) The ID for the Log Analytics Workspace to link this Container Apps Managed Environment to. +* `log_analytics_workspace_id` - (Optional) The ID for the Log Analytics Workspace to link this Container Apps Managed Environment to. + +~> **Note:** required if `logs_destination` is set to `log-analytics`. + +* `logs_destination` - (Optional) Where the application logs will be saved for this Container Apps Managed Environment. Options are `log-analytics` or `azure-monitor`. * `workload_profile` - (Optional) The profile of the workload to scope the container app execution. A `workload_profile` block as defined below. From f9bb524529054fcf615494f8b4a63eee22da41e4 Mon Sep 17 00:00:00 2001 From: Jerome Brown Date: Wed, 22 May 2024 12:26:45 +1200 Subject: [PATCH 2/8] Fix code formatting --- ...container_app_environment_resource_test.go | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index f6cd64a9508c..5de36d1f2d0b 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -243,11 +243,11 @@ provider "azurerm" { %[1]s resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - logs_destination = "azure-monitor" - infrastructure_subnet_id = azurerm_subnet.control.id + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + logs_destination = "azure-monitor" + infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true zone_redundancy_enabled = true @@ -277,7 +277,7 @@ resource "azurerm_monitor_diagnostic_setting" "test" { metric { category = "AllMetrics" - enabled = true + enabled = true } } `, r.templateVNet(data), data.RandomInteger) @@ -318,9 +318,9 @@ provider "azurerm" { %[1]s resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location workload_profile { name = "Consumption" @@ -381,10 +381,10 @@ provider "azurerm" { %[1]s resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - infrastructure_subnet_id = azurerm_subnet.control.id + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true zone_redundancy_enabled = true From 6a9334605398535b54c43a0b9aed231764cbc26c Mon Sep 17 00:00:00 2001 From: Jerome Brown Date: Tue, 4 Jun 2024 19:33:54 +1200 Subject: [PATCH 3/8] Fix validation error for legacy --- .../containerapps/container_app_environment_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index d7da0564402b..518423762dfe 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -534,7 +534,7 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") } - if env.LogAnalyticsWorkspaceId != "" && env.LogsDestination != "log-analytics" { + if env.LogAnalyticsWorkspaceId != "" && env.LogsDestination != "" && env.LogsDestination != "log-analytics" { return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") } From 6a9693cf313267c0508a3580b0ba240354021113 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Wed, 11 Dec 2024 11:28:13 +0100 Subject: [PATCH 4/8] add tsts for customizeDiff fix crash in read fixup customizeDiff logic add consts to remove magic strings --- .../container_app_environment_resource.go | 34 +++-- ...container_app_environment_resource_test.go | 124 ++++++++++++++++++ 2 files changed, 147 insertions(+), 11 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index 518423762dfe..79f2205bf860 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -24,6 +24,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) +const ( + LogsDestinationLogAnalytics string = "log-analytics" + LogsDestinationAzureMonitor string = "azure-monitor" +) + type ContainerAppEnvironmentResource struct{} type ContainerAppEnvironmentModel struct { @@ -97,11 +102,14 @@ func (r ContainerAppEnvironmentResource) Arguments() map[string]*pluginsdk.Schem }, "logs_destination": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice([]string{"log-analytics", "azure-monitor"}, false), - Description: "The destination for the application logs. Possible values are `log-analytics` or `azure-monitor`.", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + LogsDestinationLogAnalytics, + LogsDestinationAzureMonitor, + }, false), + Description: "The destination for the application logs. Possible values are `log-analytics` or `azure-monitor`.", }, "infrastructure_resource_group_name": { @@ -353,7 +361,7 @@ func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { } if appLogsConfig := props.AppLogsConfiguration; appLogsConfig != nil { - state.LogsDestination = *appLogsConfig.Destination + state.LogsDestination = pointer.From(appLogsConfig.Destination) } state.CustomDomainVerificationId = pointer.From(props.CustomDomainConfiguration.CustomDomainVerificationId) @@ -530,12 +538,16 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { } } - if env.LogsDestination == "log-analytics" && env.LogAnalyticsWorkspaceId == "" { - return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") - } + switch env.LogsDestination { + case LogsDestinationLogAnalytics: + if env.LogAnalyticsWorkspaceId == "" { + return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") + } + default: + if env.LogAnalyticsWorkspaceId != "" { + return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") + } - if env.LogAnalyticsWorkspaceId != "" && env.LogsDestination != "" && env.LogsDestination != "log-analytics" { - return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") } return nil diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index 5de36d1f2d0b..9bcb70679f1f 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -6,6 +6,7 @@ package containerapps_test import ( "context" "fmt" + "regexp" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -79,6 +80,30 @@ func TestAccContainerAppEnvironment_complete(t *testing.T) { }) } +func TestAccContainerAppEnvironment_logsDestinationWithoutWorkspaceShouldFail(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app_environment", "test") + r := ContainerAppEnvironmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.logsDestinationWithoutWorkspaceShouldFail(data), + ExpectError: regexp.MustCompile("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`"), + }, + }) +} + +func TestAccContainerAppEnvironment_logsAzureMonitorWithWorkspaceShouldFail(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app_environment", "test") + r := ContainerAppEnvironmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.logsAzureMonitorWithWorkspaceShouldFail(data), + ExpectError: regexp.MustCompile("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`"), + }, + }) +} + func TestAccContainerAppEnvironment_updateWorkloadProfile(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_container_app_environment", "test") r := ContainerAppEnvironmentResource{} @@ -283,6 +308,105 @@ resource "azurerm_monitor_diagnostic_setting" "test" { `, r.templateVNet(data), data.RandomInteger) } +func (r ContainerAppEnvironmentResource) logsDestinationWithoutWorkspaceShouldFail(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_environment" "test" { + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + logs_destination = "log-analytics" + infrastructure_subnet_id = azurerm_subnet.control.id + + internal_load_balancer_enabled = true + zone_redundancy_enabled = true + mutual_tls_enabled = true + + workload_profile { + maximum_count = 3 + minimum_count = 0 + name = "D4-01" + workload_profile_type = "D4" + } + + tags = { + Foo = "Bar" + secret = "sauce" + } +} + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "diagnostics" + target_resource_id = azurerm_container_app_environment.test.id + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + + enabled_log { + category_group = "allLogs" + } + + metric { + category = "AllMetrics" + enabled = true + } +} +`, r.templateVNet(data), data.RandomInteger) +} + +func (r ContainerAppEnvironmentResource) logsAzureMonitorWithWorkspaceShouldFail(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_environment" "test" { + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + logs_destination = "azure-monitor" + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + infrastructure_subnet_id = azurerm_subnet.control.id + + internal_load_balancer_enabled = true + zone_redundancy_enabled = true + mutual_tls_enabled = true + + workload_profile { + maximum_count = 3 + minimum_count = 0 + name = "D4-01" + workload_profile_type = "D4" + } + + tags = { + Foo = "Bar" + secret = "sauce" + } +} + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "diagnostics" + target_resource_id = azurerm_container_app_environment.test.id + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + + enabled_log { + category_group = "allLogs" + } + + metric { + category = "AllMetrics" + enabled = true + } +} +`, r.templateVNet(data), data.RandomInteger) +} + func (r ContainerAppEnvironmentResource) completeWithoutWorkloadProfile(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { From 5b1079beb00fdbd7b25c555fc39ef748ed937955 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 12 Dec 2024 16:38:23 +0100 Subject: [PATCH 5/8] attempt to reconcile new property for default value --- .../container_app_environment_resource.go | 50 +++++++++---------- ...container_app_environment_resource_test.go | 24 +++++---- .../r/container_app_environment.html.markdown | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index 79f2205bf860..91958f1fdf83 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -241,6 +241,9 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { Location: containerAppEnvironment.Location, Name: pointer.To(containerAppEnvironment.Name), Properties: &managedenvironments.ManagedEnvironmentProperties{ + AppLogsConfiguration: &managedenvironments.AppLogsConfiguration{ + Destination: pointer.To(containerAppEnvironment.LogsDestination), + }, VnetConfiguration: &managedenvironments.VnetConfiguration{}, ZoneRedundant: pointer.To(containerAppEnvironment.ZoneRedundant), PeerAuthentication: &managedenvironments.ManagedEnvironmentPropertiesPeerAuthentication{ @@ -265,13 +268,11 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { managedEnvironment.Properties.InfrastructureResourceGroup = pointer.To(containerAppEnvironment.InfrastructureResourceGroup) } - if containerAppEnvironment.LogsDestination != "" && containerAppEnvironment.LogsDestination != "log-analytics" { - managedEnvironment.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ - Destination: pointer.To(containerAppEnvironment.LogsDestination), + if containerAppEnvironment.LogAnalyticsWorkspaceId != "" { + if containerAppEnvironment.LogsDestination == LogsDestinationAzureMonitor { + return fmt.Errorf("cannot set `log_analytics_workspace_id` when `logs_destination` is %s", LogsDestinationAzureMonitor) } - } - if containerAppEnvironment.LogAnalyticsWorkspaceId != "" { logAnalyticsId, err := workspaces.ParseWorkspaceID(containerAppEnvironment.LogAnalyticsWorkspaceId) if err != nil { return err @@ -297,12 +298,9 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { if keys.Model.PrimarySharedKey == nil { return fmt.Errorf("reading shared key for %s in %s", logAnalyticsId, id) } - managedEnvironment.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ - Destination: pointer.To("log-analytics"), - LogAnalyticsConfiguration: &managedenvironments.LogAnalyticsConfiguration{ - CustomerId: workspace.Model.Properties.CustomerId, - SharedKey: keys.Model.PrimarySharedKey, - }, + managedEnvironment.Properties.AppLogsConfiguration.LogAnalyticsConfiguration = &managedenvironments.LogAnalyticsConfiguration{ + CustomerId: workspace.Model.Properties.CustomerId, + SharedKey: keys.Model.PrimarySharedKey, } } @@ -325,7 +323,7 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 5 * time.Minute, + Timeout: 35 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ContainerApps.ManagedEnvironmentClient id, err := managedenvironments.ParseManagedEnvironmentID(metadata.ResourceData.Id()) @@ -515,11 +513,6 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { return nil } - var env ContainerAppEnvironmentModel - if err := metadata.DecodeDiff(&env); err != nil { - return err - } - if metadata.ResourceDiff.HasChange("workload_profile") { oldProfiles, newProfiles := metadata.ResourceDiff.GetChange("workload_profile") @@ -538,16 +531,21 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { } } - switch env.LogsDestination { - case LogsDestinationLogAnalytics: - if env.LogAnalyticsWorkspaceId == "" { - return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") - } - default: - if env.LogAnalyticsWorkspaceId != "" { - return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") - } + // Note: this only protects at plan time against updates - Create with the wrong configuration will fail with an error on Apply + if metadata.ResourceDiff.HasChanges("logs_destination", "log_analytics_workspace_id") { + logsDestination := metadata.ResourceDiff.Get("logs_destination").(string) + logAnalyticsWorkspaceID := metadata.ResourceDiff.Get("log_analytics_workspace_id").(string) + switch logsDestination { + case LogsDestinationLogAnalytics: + if logAnalyticsWorkspaceID == "" { + return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") + } + default: + if logAnalyticsWorkspaceID != "" { + return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") + } + } } return nil diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index 9bcb70679f1f..9efcb47e94ea 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -87,6 +87,7 @@ func TestAccContainerAppEnvironment_logsDestinationWithoutWorkspaceShouldFail(t data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.logsDestinationWithoutWorkspaceShouldFail(data), + PlanOnly: true, ExpectError: regexp.MustCompile("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`"), }, }) @@ -99,6 +100,7 @@ func TestAccContainerAppEnvironment_logsAzureMonitorWithWorkspaceShouldFail(t *t data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.logsAzureMonitorWithWorkspaceShouldFail(data), + PlanOnly: true, ExpectError: regexp.MustCompile("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`"), }, }) @@ -268,11 +270,12 @@ provider "azurerm" { %[1]s resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - logs_destination = "azure-monitor" - infrastructure_subnet_id = azurerm_subnet.control.id + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + logs_destination = "log-analytics" + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true zone_redundancy_enabled = true @@ -471,12 +474,11 @@ resource "azurerm_log_analytics_workspace" "second" { } resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - logs_destination = "log-analytics" - log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id - infrastructure_subnet_id = azurerm_subnet.control.id + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + logs_destination = "azure-monitor" + infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true zone_redundancy_enabled = true diff --git a/website/docs/r/container_app_environment.html.markdown b/website/docs/r/container_app_environment.html.markdown index b7bb12711c26..4705c4784cf1 100644 --- a/website/docs/r/container_app_environment.html.markdown +++ b/website/docs/r/container_app_environment.html.markdown @@ -67,7 +67,7 @@ The following arguments are supported: * `log_analytics_workspace_id` - (Optional) The ID for the Log Analytics Workspace to link this Container Apps Managed Environment to. -~> **Note:** required if `logs_destination` is set to `log-analytics`. +~> **Note:** required if `logs_destination` is set to `log-analytics`. Cannot be set if `logs_destination` is set to `azure-monito`. * `logs_destination` - (Optional) Where the application logs will be saved for this Container Apps Managed Environment. Options are `log-analytics` or `azure-monitor`. From 38c68c86ca2bc66b2520b9efdac1b6da22f212b7 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Tue, 28 Jan 2025 13:49:27 +0100 Subject: [PATCH 6/8] Add default for `logs_destination` to remove `Computed` Add helper function for finding `log_analytics_workspace_id` from `customerID` Add helper function for getting Workspace (primary) shared key Fixup customiseDiff for new/existing resources changes to `log_analytics_workspace_id` and `logs_destination` Remove ignore for `log_analytics_workspace_id` on data.ImportStep for all tests as this is now discovered correctly --- .../container_app_environment_resource.go | 179 +++++++++++------- ...container_app_environment_resource_test.go | 96 +++++++++- 2 files changed, 198 insertions(+), 77 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index 91958f1fdf83..5d0d58945268 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -6,6 +6,7 @@ package containerapps import ( "context" "fmt" + "strings" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -27,6 +28,7 @@ import ( const ( LogsDestinationLogAnalytics string = "log-analytics" LogsDestinationAzureMonitor string = "azure-monitor" + LogsDestinationAzureNone string = "" ) type ContainerAppEnvironmentResource struct{} @@ -104,7 +106,7 @@ func (r ContainerAppEnvironmentResource) Arguments() map[string]*pluginsdk.Schem "logs_destination": { Type: pluginsdk.TypeString, Optional: true, - Computed: true, + Default: LogsDestinationAzureNone, ValidateFunc: validation.StringInSlice([]string{ LogsDestinationLogAnalytics, LogsDestinationAzureMonitor, @@ -215,7 +217,6 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ContainerApps.ManagedEnvironmentClient - logAnalyticsClient := metadata.Client.LogAnalytics.SharedKeyWorkspacesClient subscriptionId := metadata.Client.Account.SubscriptionId var containerAppEnvironment ContainerAppEnvironmentModel @@ -273,34 +274,14 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { return fmt.Errorf("cannot set `log_analytics_workspace_id` when `logs_destination` is %s", LogsDestinationAzureMonitor) } - logAnalyticsId, err := workspaces.ParseWorkspaceID(containerAppEnvironment.LogAnalyticsWorkspaceId) + customerId, sharedKey, err := getSharedKeyForWorkspace(ctx, metadata, containerAppEnvironment.LogAnalyticsWorkspaceId) if err != nil { - return err + return fmt.Errorf("retrieving access keys to Log Analytics Workspace for %s: %+v", id, err) } - workspace, err := logAnalyticsClient.Get(ctx, *logAnalyticsId) - if err != nil { - return fmt.Errorf("retrieving %s for %s: %+v", logAnalyticsId, id, err) - } - - if workspace.Model == nil || workspace.Model.Properties == nil { - return fmt.Errorf("reading customer ID from %s", logAnalyticsId) - } - - if workspace.Model.Properties.CustomerId == nil { - return fmt.Errorf("reading customer ID from %s, `customer_id` is nil", logAnalyticsId) - } - - keys, err := logAnalyticsClient.SharedKeysGetSharedKeys(ctx, *logAnalyticsId) - if err != nil { - return fmt.Errorf("retrieving access keys to %s for %s: %+v", logAnalyticsId, id, err) - } - if keys.Model.PrimarySharedKey == nil { - return fmt.Errorf("reading shared key for %s in %s", logAnalyticsId, id) - } managedEnvironment.Properties.AppLogsConfiguration.LogAnalyticsConfiguration = &managedenvironments.LogAnalyticsConfiguration{ - CustomerId: workspace.Model.Properties.CustomerId, - SharedKey: keys.Model.PrimarySharedKey, + CustomerId: customerId, + SharedKey: sharedKey, } } @@ -323,7 +304,7 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 35 * time.Minute, + Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ContainerApps.ManagedEnvironmentClient id, err := managedenvironments.ParseManagedEnvironmentID(metadata.ResourceData.Id()) @@ -360,6 +341,14 @@ func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { if appLogsConfig := props.AppLogsConfiguration; appLogsConfig != nil { state.LogsDestination = pointer.From(appLogsConfig.Destination) + if appLogsConfig.LogAnalyticsConfiguration != nil && appLogsConfig.LogAnalyticsConfiguration.CustomerId != nil { + workspaceId, err := findWorkspaceResourceIDFromCustomerID(ctx, metadata, *appLogsConfig.LogAnalyticsConfiguration.CustomerId) + if err != nil { + return fmt.Errorf("retrieving Log Analytics Workspace ID for %s: %+v", id, err) + } + + state.LogAnalyticsWorkspaceId = workspaceId.ID() + } } state.CustomDomainVerificationId = pointer.From(props.CustomDomainConfiguration.CustomDomainVerificationId) @@ -377,12 +366,7 @@ func (r ContainerAppEnvironmentResource) Read() sdk.ResourceFunc { state.DaprApplicationInsightsConnectionString = v } - // Reading in log_analytics_workspace_id is not possible, so reading from config. Import will need to ignore_changes unfortunately - if v := metadata.ResourceData.Get("log_analytics_workspace_id").(string); v != "" { - state.LogAnalyticsWorkspaceId = v - } - - if err := metadata.Encode(&state); err != nil { + if err = metadata.Encode(&state); err != nil { return fmt.Errorf("encoding: %+v", err) } @@ -414,7 +398,6 @@ func (r ContainerAppEnvironmentResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - logAnalyticsClient := metadata.Client.LogAnalytics.SharedKeyWorkspacesClient client := metadata.Client.ContainerApps.ManagedEnvironmentClient id, err := managedenvironments.ParseManagedEnvironmentID(metadata.ResourceData.Id()) if err != nil { @@ -444,41 +427,29 @@ func (r ContainerAppEnvironmentResource) Update() sdk.ResourceFunc { existing.Model.Properties.PeerTrafficConfiguration.Encryption.Enabled = pointer.To(state.Mtls) } - // (@jackofallops) This is not updatable and needs to be removed since the read does not return the sensitive Key field. - // Whilst not ideal, this means we don't need to try and retrieve it again just to send a no-op. - existing.Model.Properties.AppLogsConfiguration = nil - if metadata.ResourceData.Get("log_analytics_workspace_id") != "" { - logAnalyticsId, err := workspaces.ParseWorkspaceID(metadata.ResourceData.Get("log_analytics_workspace_id").(string)) - if err != nil { - return err - } - - workspace, err := logAnalyticsClient.Get(ctx, *logAnalyticsId) - if err != nil { - return fmt.Errorf("retrieving %s for %s: %+v", logAnalyticsId, id, err) - } - - if workspace.Model == nil || workspace.Model.Properties == nil { - return fmt.Errorf("reading customer ID from %s", logAnalyticsId) - } + if metadata.ResourceData.HasChanges("logs_destination", "log_analytics_workspace_id") { + switch state.LogsDestination { + case LogsDestinationAzureMonitor: + existing.Model.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ + Destination: pointer.To(LogsDestinationAzureMonitor), + LogAnalyticsConfiguration: nil, + } + case LogsDestinationLogAnalytics: + customerId, sharedKey, err := getSharedKeyForWorkspace(ctx, metadata, state.LogAnalyticsWorkspaceId) + if err != nil { + return fmt.Errorf("retrieving access keys to Log Analytics Workspace for %s: %+v", id, err) + } - if workspace.Model.Properties.CustomerId == nil { - return fmt.Errorf("reading customer ID from %s, `customer_id` is nil", logAnalyticsId) - } + existing.Model.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ + Destination: pointer.To(LogsDestinationLogAnalytics), + LogAnalyticsConfiguration: &managedenvironments.LogAnalyticsConfiguration{ + CustomerId: customerId, + SharedKey: sharedKey, + }, + } - keys, err := logAnalyticsClient.SharedKeysGetSharedKeys(ctx, *logAnalyticsId) - if err != nil { - return fmt.Errorf("retrieving access keys to %s for %s: %+v", logAnalyticsId, id, err) - } - if keys.Model.PrimarySharedKey == nil { - return fmt.Errorf("reading shared key for %s in %s", logAnalyticsId, id) - } - existing.Model.Properties.AppLogsConfiguration = &managedenvironments.AppLogsConfiguration{ - Destination: pointer.To("log-analytics"), - LogAnalyticsConfiguration: &managedenvironments.LogAnalyticsConfiguration{ - CustomerId: workspace.Model.Properties.CustomerId, - SharedKey: keys.Model.PrimarySharedKey, - }, + default: + existing.Model.Properties.AppLogsConfiguration = nil } } @@ -531,18 +502,19 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { } } - // Note: this only protects at plan time against updates - Create with the wrong configuration will fail with an error on Apply if metadata.ResourceDiff.HasChanges("logs_destination", "log_analytics_workspace_id") { logsDestination := metadata.ResourceDiff.Get("logs_destination").(string) logAnalyticsWorkspaceID := metadata.ResourceDiff.Get("log_analytics_workspace_id").(string) + logAnalyticsWorkspaceIDIsNull := metadata.ResourceDiff.GetRawConfig().AsValueMap()["log_analytics_workspace_id"].IsNull() + switch logsDestination { case LogsDestinationLogAnalytics: - if logAnalyticsWorkspaceID == "" { + if logAnalyticsWorkspaceIDIsNull { return fmt.Errorf("`log_analytics_workspace_id` must be set when `logs_destination` is set to `log-analytics`") } - default: - if logAnalyticsWorkspaceID != "" { + case LogsDestinationAzureMonitor, LogsDestinationAzureNone: + if logAnalyticsWorkspaceID != "" || !logAnalyticsWorkspaceIDIsNull { return fmt.Errorf("`log_analytics_workspace_id` can only be set when `logs_destination` is set to `log-analytics`") } } @@ -552,3 +524,68 @@ func (r ContainerAppEnvironmentResource) CustomizeDiff() sdk.ResourceFunc { }, } } + +func findWorkspaceResourceIDFromCustomerID(ctx context.Context, meta sdk.ResourceMetaData, customerID string) (*workspaces.WorkspaceId, error) { + client := meta.Client.LogAnalytics.WorkspaceClient + + subscriptionId := commonids.NewSubscriptionID(meta.Client.Account.SubscriptionId) + + result := &workspaces.WorkspaceId{} + + list, err := client.List(ctx, subscriptionId) + if err != nil { + return nil, err + } + + model := list.Model + if model == nil { + return nil, fmt.Errorf("could not resolve Log Analytics Workspace ID for %s, list model was nil", customerID) + } + + if model.Value == nil || len(*model.Value) == 0 { + return nil, fmt.Errorf("could not resolve Log Analytics Workspace ID for %s, no Log Analytics Workspaces found in %s", customerID, subscriptionId) + } + + for _, v := range *list.Model.Value { + if v.Properties != nil && v.Properties.CustomerId != nil && strings.EqualFold(*v.Properties.CustomerId, customerID) { + result, err = workspaces.ParseWorkspaceIDInsensitively(pointer.From(v.Id)) + if err != nil { + return nil, err + } + } + } + + return result, nil +} + +func getSharedKeyForWorkspace(ctx context.Context, meta sdk.ResourceMetaData, workspaceID string) (*string, *string, error) { + logAnalyticsClient := meta.Client.LogAnalytics.SharedKeyWorkspacesClient + + logAnalyticsId, err := workspaces.ParseWorkspaceID(workspaceID) + if err != nil { + return nil, nil, err + } + + workspace, err := logAnalyticsClient.Get(ctx, *logAnalyticsId) + if err != nil { + return nil, nil, fmt.Errorf("retrieving %s: %+v", logAnalyticsId, err) + } + + if workspace.Model == nil || workspace.Model.Properties == nil { + return nil, nil, fmt.Errorf("reading customer ID from %s", logAnalyticsId) + } + + if workspace.Model.Properties.CustomerId == nil { + return nil, nil, fmt.Errorf("reading customer ID from %s, `customer_id` is nil", logAnalyticsId) + } + + keys, err := logAnalyticsClient.SharedKeysGetSharedKeys(ctx, *logAnalyticsId) + if err != nil { + return nil, nil, fmt.Errorf("retrieving access keys to %s: %+v", logAnalyticsId, err) + } + if keys.Model.PrimarySharedKey == nil { + return nil, nil, fmt.Errorf("reading shared key for %s", logAnalyticsId) + } + + return workspace.Model.Properties.CustomerId, keys.Model.PrimarySharedKey, nil +} diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index 9efcb47e94ea..2a9d8a8a6fcb 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -76,7 +76,7 @@ func TestAccContainerAppEnvironment_complete(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), }) } @@ -106,6 +106,42 @@ func TestAccContainerAppEnvironment_logsAzureMonitorWithWorkspaceShouldFail(t *t }) } +func TestAccContainerAppEnvironment_updateLogsDestination(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app_environment", "test") + r := ContainerAppEnvironmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.completeUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.completeNoLoggingDestination(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccContainerAppEnvironment_updateWorkloadProfile(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_container_app_environment", "test") r := ContainerAppEnvironmentResource{} @@ -117,28 +153,28 @@ func TestAccContainerAppEnvironment_updateWorkloadProfile(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), { Config: r.completeUpdate(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), { Config: r.completeMultipleWorkloadProfiles(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), { Config: r.complete(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), }) } @@ -168,7 +204,7 @@ func TestAccContainerAppEnvironment_zoneRedundant(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep("log_analytics_workspace_id"), + data.ImportStep(), }) } @@ -311,6 +347,54 @@ resource "azurerm_monitor_diagnostic_setting" "test" { `, r.templateVNet(data), data.RandomInteger) } +func (r ContainerAppEnvironmentResource) completeNoLoggingDestination(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_environment" "test" { + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + infrastructure_subnet_id = azurerm_subnet.control.id + + internal_load_balancer_enabled = true + zone_redundancy_enabled = true + mutual_tls_enabled = true + + workload_profile { + maximum_count = 3 + minimum_count = 0 + name = "D4-01" + workload_profile_type = "D4" + } + + tags = { + Foo = "Bar" + secret = "sauce" + } +} + +resource "azurerm_monitor_diagnostic_setting" "test" { + name = "diagnostics" + target_resource_id = azurerm_container_app_environment.test.id + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + + enabled_log { + category_group = "allLogs" + } + + metric { + category = "AllMetrics" + enabled = true + } +} +`, r.templateVNet(data), data.RandomInteger) +} + func (r ContainerAppEnvironmentResource) logsDestinationWithoutWorkspaceShouldFail(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { From 864bfb8ef153b57919c80d9d7dd4746826182a29 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Tue, 28 Jan 2025 14:01:55 +0100 Subject: [PATCH 7/8] terrafmt --- .../container_app_environment_resource_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource_test.go b/internal/services/containerapps/container_app_environment_resource_test.go index 2a9d8a8a6fcb..bfd492a12bfd 100644 --- a/internal/services/containerapps/container_app_environment_resource_test.go +++ b/internal/services/containerapps/container_app_environment_resource_test.go @@ -356,10 +356,10 @@ provider "azurerm" { %[1]s resource "azurerm_container_app_environment" "test" { - name = "acctest-CAEnv%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - infrastructure_subnet_id = azurerm_subnet.control.id + name = "acctest-CAEnv%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + infrastructure_subnet_id = azurerm_subnet.control.id internal_load_balancer_enabled = true zone_redundancy_enabled = true From 62bb19da21b02a267139a572a536154517e5de82 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Wed, 29 Jan 2025 11:18:59 +0100 Subject: [PATCH 8/8] review comments addressed --- .../containerapps/container_app_environment_resource.go | 4 ---- website/docs/r/container_app_environment.html.markdown | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/services/containerapps/container_app_environment_resource.go b/internal/services/containerapps/container_app_environment_resource.go index 5d0d58945268..c61cb9cd4906 100644 --- a/internal/services/containerapps/container_app_environment_resource.go +++ b/internal/services/containerapps/container_app_environment_resource.go @@ -270,10 +270,6 @@ func (r ContainerAppEnvironmentResource) Create() sdk.ResourceFunc { } if containerAppEnvironment.LogAnalyticsWorkspaceId != "" { - if containerAppEnvironment.LogsDestination == LogsDestinationAzureMonitor { - return fmt.Errorf("cannot set `log_analytics_workspace_id` when `logs_destination` is %s", LogsDestinationAzureMonitor) - } - customerId, sharedKey, err := getSharedKeyForWorkspace(ctx, metadata, containerAppEnvironment.LogAnalyticsWorkspaceId) if err != nil { return fmt.Errorf("retrieving access keys to Log Analytics Workspace for %s: %+v", id, err) diff --git a/website/docs/r/container_app_environment.html.markdown b/website/docs/r/container_app_environment.html.markdown index 4705c4784cf1..097a7eac5ed8 100644 --- a/website/docs/r/container_app_environment.html.markdown +++ b/website/docs/r/container_app_environment.html.markdown @@ -67,7 +67,7 @@ The following arguments are supported: * `log_analytics_workspace_id` - (Optional) The ID for the Log Analytics Workspace to link this Container Apps Managed Environment to. -~> **Note:** required if `logs_destination` is set to `log-analytics`. Cannot be set if `logs_destination` is set to `azure-monito`. +~> **Note:** required if `logs_destination` is set to `log-analytics`. Cannot be set if `logs_destination` is set to `azure-monitor`. * `logs_destination` - (Optional) Where the application logs will be saved for this Container Apps Managed Environment. Options are `log-analytics` or `azure-monitor`.