From eab397ef0b6eb5ffb9fd5020b5c9770bd2f6c750 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Tue, 11 Jun 2024 10:18:53 +0800 Subject: [PATCH 01/19] add new features --- internal/services/serviceconnector/helper.go | 89 ++++++++++++++++ .../service_connector_app_service_resource.go | 100 ++++++++++++++++-- ...ice_connector_app_service_resource_test.go | 12 +++ 3 files changed, 194 insertions(+), 7 deletions(-) diff --git a/internal/services/serviceconnector/helper.go b/internal/services/serviceconnector/helper.go index 8df20170be5a..ec44f59a101d 100644 --- a/internal/services/serviceconnector/helper.go +++ b/internal/services/serviceconnector/helper.go @@ -31,6 +31,19 @@ type SecretStoreModel struct { KeyVaultId string `tfschema:"key_vault_id"` } +type ConfigurationInfo struct { + Action string `tfschema:"action"` + ConfigurationStore []ConfigurationStore `tfschema:"configuration_store"` +} + +type ConfigurationStore struct { + AppConfigurationId string `tfschema:"app_configuration_id"` +} + +type PublicNetworkSolution struct { + Action string `tfschema:"action"` +} + func secretStoreSchema() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeList, @@ -308,6 +321,82 @@ func expandSecretStore(input []SecretStoreModel) *servicelinker.SecretStore { } } +func expandConfigurationInfo(input []ConfigurationInfo) *servicelinker.ConfigurationInfo { + if len(input) == 0 { + return nil + } + v := input[0] + action := servicelinker.ActionType(v.Action) + configurationStore := expandConfigurationStore(v.ConfigurationStore) + + return &servicelinker.ConfigurationInfo{ + Action: pointer.To(action), + ConfigurationStore: configurationStore, + } +} + +func expandConfigurationStore(input []ConfigurationStore) *servicelinker.ConfigurationStore { + if len(input) == 0 { + return nil + } + v := input[0] + + appConfigurationId := v.AppConfigurationId + return &servicelinker.ConfigurationStore{ + AppConfigurationId: utils.String(appConfigurationId), + } +} + +func expandPublicNetworkSolution(input []PublicNetworkSolution) *servicelinker.PublicNetworkSolution { + if len(input) == 0 { + return nil + } + v := input[0] + + action := v.Action + return &servicelinker.PublicNetworkSolution{ + Action: pointer.To(servicelinker.ActionType(action)), + } +} + +func flattenConfigurationInfo(input servicelinker.ConfigurationInfo) []ConfigurationInfo { + var action string + var configurationStore []ConfigurationStore + + if input.Action != nil { + action = string(*input.Action) + } + + if input.ConfigurationStore != nil { + configurationStore = []ConfigurationStore{ + { + AppConfigurationId: pointer.From(input.ConfigurationStore.AppConfigurationId), + }, + } + } + + return []ConfigurationInfo{ + { + Action: action, + ConfigurationStore: configurationStore, + }, + } +} + +func flattenPublicNetworkSolution(input servicelinker.PublicNetworkSolution) []PublicNetworkSolution { + var action string + + if input.Action != nil { + action = string(*input.Action) + } + + return []PublicNetworkSolution{ + { + Action: action, + }, + } +} + func flattenServiceConnectorAuthInfo(input servicelinker.AuthInfoBase, pwd string) []AuthInfoModel { var authType string var name string diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index 04a7a2d82f49..51c40fef2c56 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -27,13 +27,16 @@ var _ sdk.ResourceWithUpdate = AppServiceConnectorResource{} type AppServiceConnectorResource struct{} type AppServiceConnectorResourceModel struct { - Name string `tfschema:"name"` - AppServiceId string `tfschema:"app_service_id"` - TargetResourceId string `tfschema:"target_resource_id"` - ClientType string `tfschema:"client_type"` - AuthInfo []AuthInfoModel `tfschema:"authentication"` - VnetSolution string `tfschema:"vnet_solution"` - SecretStore []SecretStoreModel `tfschema:"secret_store"` + Name string `tfschema:"name"` + AppServiceId string `tfschema:"app_service_id"` + TargetResourceId string `tfschema:"target_resource_id"` + ClientType string `tfschema:"client_type"` + AuthInfo []AuthInfoModel `tfschema:"authentication"` + VnetSolution string `tfschema:"vnet_solution"` + SecretStore []SecretStoreModel `tfschema:"secret_store"` + Scope string `tfschema:"scope"` + ConfigurationInfo []ConfigurationInfo `tfschema:"configuration_info"` + PublicNetworkSolution []PublicNetworkSolution `tfschema:"public_network_solution"` } func (r AppServiceConnectorResource) Arguments() map[string]*schema.Schema { @@ -89,6 +92,61 @@ func (r AppServiceConnectorResource) Arguments() map[string]*schema.Schema { }, "authentication": authInfoSchema(), + + "scope": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "configuration_info": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(servicelinker.ActionTypeOptOut), + string(servicelinker.ActionTypeEnable), + }, false), + }, + + "configuration_store": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "app_configuration_id": { + Type: pluginsdk.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + + "public_network_solution": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(servicelinker.ActionTypeOptOut), + string(servicelinker.ActionTypeEnable), + }, false), + }, + }, + }, + }, } } @@ -163,6 +221,18 @@ func (r AppServiceConnectorResource) Create() sdk.ResourceFunc { serviceConnectorProperties.VNetSolution = &vNetSolution } + if model.Scope != "" { + serviceConnectorProperties.Scope = pointer.To(model.Scope) + } + + if model.ConfigurationInfo != nil { + serviceConnectorProperties.ConfigurationInfo = expandConfigurationInfo(model.ConfigurationInfo) + } + + if model.PublicNetworkSolution != nil { + serviceConnectorProperties.PublicNetworkSolution = expandPublicNetworkSolution(model.PublicNetworkSolution) + } + props := servicelinker.LinkerResource{ Id: utils.String(id.ID()), Name: utils.String(model.Name), @@ -224,6 +294,18 @@ func (r AppServiceConnectorResource) Read() sdk.ResourceFunc { state.SecretStore = flattenSecretStore(*props.SecretStore) } + if props.Scope != nil { + state.Scope = pointer.From(props.Scope) + } + + if props.ConfigurationInfo != nil { + state.ConfigurationInfo = flattenConfigurationInfo(pointer.From(props.ConfigurationInfo)) + } + + if props.PublicNetworkSolution != nil { + state.PublicNetworkSolution = flattenPublicNetworkSolution(pointer.From(props.PublicNetworkSolution)) + } + return metadata.Encode(&state) } return nil @@ -296,6 +378,10 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { linkerProps.AuthInfo = authInfo } + if d.HasChange("scope") { + linkerProps.Scope = pointer.To(state.Scope) + } + props := links.LinkerPatch{ Properties: &linkerProps, } diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index d490b0b3cef3..b8a613155a58 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -479,6 +479,8 @@ resource "azurerm_linux_web_app" "test" { app_settings, identity, sticky_settings, + ftp_publish_basic_authentication_enabled, + webdeploy_publish_basic_authentication_enabled, ] } } @@ -492,6 +494,16 @@ resource "azurerm_app_service_connection" "test" { authentication { type = "systemAssignedIdentity" } + scope = "default" + configuration_info { + action = "optOut" + configuration_store { + app_configuration_id = "foo" + } + } + public_network_solution { + action = "enable" + } } `, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) } From 49f6522ad359174603529da0abf6ef9871b1b987 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Wed, 19 Jun 2024 15:18:33 +0800 Subject: [PATCH 02/19] azurerm_app_service_connection: Add support for scope, configuration_info and public_network_solution --- ...ice_connector_app_service_resource_test.go | 14 +++++----- .../r/app_service_connection.html.markdown | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index b8a613155a58..466232f24faa 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -479,8 +479,8 @@ resource "azurerm_linux_web_app" "test" { app_settings, identity, sticky_settings, - ftp_publish_basic_authentication_enabled, - webdeploy_publish_basic_authentication_enabled, + ftp_publish_basic_authentication_enabled, + webdeploy_publish_basic_authentication_enabled, ] } } @@ -496,13 +496,13 @@ resource "azurerm_app_service_connection" "test" { } scope = "default" configuration_info { - action = "optOut" - configuration_store { - app_configuration_id = "foo" - } + action = "optOut" + configuration_store { + app_configuration_id = "foo" + } } public_network_solution { - action = "enable" + action = "enable" } } `, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) diff --git a/website/docs/r/app_service_connection.html.markdown b/website/docs/r/app_service_connection.html.markdown index 2301f7df271c..07be023fa2e1 100644 --- a/website/docs/r/app_service_connection.html.markdown +++ b/website/docs/r/app_service_connection.html.markdown @@ -112,6 +112,34 @@ An `authentication` block supports the following: --- +* `scope` - (Optional) Connection scope in source service. + +* `configuration_info` - (Optional) The connection information consumed by applications, including secrets, connection strings. A `configuration_info` block as defined below. + +--- + +A `configuration_info` block supports the following: + +* `action` - (Optional) The action indicates whether to apply configurations on source application. Possible values are `enable` and `optOut`. + +* `configuration_store` - (Optional) A `configuration_store` block as defined below. + +--- + +* `app_configuration_id` - (Optional) The app configuration id to store configuration. + +--- + +* `public_network_solution` - (Optional) A `public_network_solution` block as defined below. + +--- + +* A `public_network_solution` block supports the following: + +* `action` - (Optional) Indicates public network solution. Possible values are `enable` and `optOut`. + +--- + * `client_type` - (Optional) The application client type. Possible values are `none`, `dotnet`, `java`, `python`, `go`, `php`, `ruby`, `django`, `nodejs` and `springBoot`. Defaults to `none`. * `vnet_solution` - (Optional) The type of the VNet solution. Possible values are `serviceEndpoint`, `privateLink`. From ddbe4c63f1218f6763474e4b9bb6cf59b9ee694d Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Fri, 26 Jul 2024 17:32:38 +0800 Subject: [PATCH 03/19] Rename attribute --- .../service_connector_app_service_resource.go | 4 ++-- .../service_connector_app_service_resource_test.go | 2 +- website/docs/r/app_service_connection.html.markdown | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index 51c40fef2c56..07160174055e 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -35,7 +35,7 @@ type AppServiceConnectorResourceModel struct { VnetSolution string `tfschema:"vnet_solution"` SecretStore []SecretStoreModel `tfschema:"secret_store"` Scope string `tfschema:"scope"` - ConfigurationInfo []ConfigurationInfo `tfschema:"configuration_info"` + ConfigurationInfo []ConfigurationInfo `tfschema:"configuration"` PublicNetworkSolution []PublicNetworkSolution `tfschema:"public_network_solution"` } @@ -98,7 +98,7 @@ func (r AppServiceConnectorResource) Arguments() map[string]*schema.Schema { Optional: true, }, - "configuration_info": { + "configuration": { Type: pluginsdk.TypeList, Optional: true, MaxItems: 1, diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 466232f24faa..0adcd1a51cdc 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -495,7 +495,7 @@ resource "azurerm_app_service_connection" "test" { type = "systemAssignedIdentity" } scope = "default" - configuration_info { + configuration { action = "optOut" configuration_store { app_configuration_id = "foo" diff --git a/website/docs/r/app_service_connection.html.markdown b/website/docs/r/app_service_connection.html.markdown index 07be023fa2e1..444f68bf2a6f 100644 --- a/website/docs/r/app_service_connection.html.markdown +++ b/website/docs/r/app_service_connection.html.markdown @@ -114,11 +114,11 @@ An `authentication` block supports the following: * `scope` - (Optional) Connection scope in source service. -* `configuration_info` - (Optional) The connection information consumed by applications, including secrets, connection strings. A `configuration_info` block as defined below. +* `configuration` - (Optional) The connection information consumed by applications, including secrets, connection strings. A `configuration` block as defined below. --- -A `configuration_info` block supports the following: +A `configuration` block supports the following: * `action` - (Optional) The action indicates whether to apply configurations on source application. Possible values are `enable` and `optOut`. From 49ca608ba5edb152687e5a1277d237858ae987cb Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Wed, 31 Jul 2024 17:32:44 +0800 Subject: [PATCH 04/19] fix failing acc test --- .../service_connector_app_service_resource_test.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 0adcd1a51cdc..57ad711ff2f3 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -485,6 +485,13 @@ resource "azurerm_linux_web_app" "test" { } } +resource "azurerm_app_configuration" "test" { + name = "testacc-appconf%[3]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "free" +} + resource "azurerm_app_service_connection" "test" { name = "acctestserviceconnector%[3]d" app_service_id = azurerm_linux_web_app.test.id @@ -496,9 +503,9 @@ resource "azurerm_app_service_connection" "test" { } scope = "default" configuration { - action = "optOut" + action = "enable" configuration_store { - app_configuration_id = "foo" + app_configuration_id = azurerm_app_configuration.test.id } } public_network_solution { From 75f78a522644ac2659a067475d21f2db3dc42f07 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Wed, 7 Aug 2024 16:55:52 +0800 Subject: [PATCH 05/19] remove unnecessary nil check --- .../service_connector_app_service_resource.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index 07160174055e..e482446b5e30 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -294,9 +294,7 @@ func (r AppServiceConnectorResource) Read() sdk.ResourceFunc { state.SecretStore = flattenSecretStore(*props.SecretStore) } - if props.Scope != nil { - state.Scope = pointer.From(props.Scope) - } + state.Scope = pointer.From(props.Scope) if props.ConfigurationInfo != nil { state.ConfigurationInfo = flattenConfigurationInfo(pointer.From(props.ConfigurationInfo)) From c10e07065f45f47374193aba1ae6c42b8fcf9819 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 15 Aug 2024 18:02:57 +0800 Subject: [PATCH 06/19] Change public network solution action to required field --- .../serviceconnector/service_connector_app_service_resource.go | 2 +- website/docs/r/app_service_connection.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index e482446b5e30..8e2a3f34d038 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -138,7 +138,7 @@ func (r AppServiceConnectorResource) Arguments() map[string]*schema.Schema { Schema: map[string]*schema.Schema{ "action": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ValidateFunc: validation.StringInSlice([]string{ string(servicelinker.ActionTypeOptOut), string(servicelinker.ActionTypeEnable), diff --git a/website/docs/r/app_service_connection.html.markdown b/website/docs/r/app_service_connection.html.markdown index 444f68bf2a6f..a05642542eaf 100644 --- a/website/docs/r/app_service_connection.html.markdown +++ b/website/docs/r/app_service_connection.html.markdown @@ -136,7 +136,7 @@ A `configuration` block supports the following: * A `public_network_solution` block supports the following: -* `action` - (Optional) Indicates public network solution. Possible values are `enable` and `optOut`. +* `action` - (Required) Indicates public network solution. Possible values are `enable` and `optOut`. --- From 5e76f4adf25130e76267115445194d81e82e1a9c Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Tue, 27 Aug 2024 10:50:32 +0800 Subject: [PATCH 07/19] add description for attributes in doc --- website/docs/r/app_service_connection.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/app_service_connection.html.markdown b/website/docs/r/app_service_connection.html.markdown index a05642542eaf..bdd3b4093a5a 100644 --- a/website/docs/r/app_service_connection.html.markdown +++ b/website/docs/r/app_service_connection.html.markdown @@ -112,7 +112,7 @@ An `authentication` block supports the following: --- -* `scope` - (Optional) Connection scope in source service. +* `scope` - (Optional) Connection scope in source service. It's namespace name if source is AKS cluster, container name if source is Container app. * `configuration` - (Optional) The connection information consumed by applications, including secrets, connection strings. A `configuration` block as defined below. From 9d1755f652f55be778a9b21b44c517ed6ffe7baf Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 19 Sep 2024 13:07:57 +0800 Subject: [PATCH 08/19] fix conflicts --- .../service_connector_app_service_resource.go | 37 +++++++++++------- ...ice_connector_app_service_resource_test.go | 39 +++++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index 8e2a3f34d038..beaad2f466f8 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -336,8 +336,9 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.ServiceConnector.LinksClient - id, err := links.ParseScopedLinkerID(metadata.ResourceData.Id()) + client := metadata.Client.ServiceConnector.ServiceLinkerClient + + id, err := servicelinker.ParseScopedLinkerID(metadata.ResourceData.Id()) if err != nil { return err } @@ -347,24 +348,26 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { return fmt.Errorf("decoding %+v", err) } - linkerProps := links.LinkerProperties{} + existing, err := client.LinkerGet(ctx, *id) + if err != nil { + return fmt.Errorf("checking for presence of existing %s: %+v", *id, err) + } + linkerProps := existing.Model.Properties d := metadata.ResourceData if d.HasChange("client_type") { - clientType := links.ClientType(state.ClientType) - linkerProps.ClientType = &clientType + linkerProps.ClientType = pointer.To(servicelinker.ClientType(state.ClientType)) } if d.HasChange("vnet_solution") { - vnetSolutionType := links.VNetSolutionType(state.VnetSolution) - vnetSolution := links.VNetSolution{ - Type: &vnetSolutionType, + vnetSolution := servicelinker.VNetSolution{ + Type: pointer.To(servicelinker.VNetSolutionType(state.VnetSolution)), } - linkerProps.VNetSolution = &vnetSolution + linkerProps.VNetSolution = pointer.To(vnetSolution) } if d.HasChange("secret_store") { - linkerProps.SecretStore = pointer.To(links.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) + linkerProps.SecretStore = pointer.To(servicelinker.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) } if d.HasChange("authentication") { @@ -380,11 +383,19 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { linkerProps.Scope = pointer.To(state.Scope) } - props := links.LinkerPatch{ - Properties: &linkerProps, + if d.HasChange("configuration") { + linkerProps.ConfigurationInfo = expandConfigurationInfo(state.ConfigurationInfo) + } + + if d.HasChange("public_network_solution") { + linkerProps.PublicNetworkSolution = expandPublicNetworkSolution(state.PublicNetworkSolution) + } + + props := servicelinker.LinkerResource{ + Properties: linkerProps, } - if err := client.LinkerUpdateThenPoll(ctx, *id, props); err != nil { + if err := client.LinkerCreateOrUpdateThenPoll(ctx, *id, props); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 57ad711ff2f3..48d2e82a5136 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -180,6 +180,8 @@ resource "azurerm_linux_web_app" "test" { app_settings["AZURE_STORAGEBLOB_RESOURCEENDPOINT"], identity, sticky_settings, + ftp_publish_basic_authentication_enabled, + webdeploy_publish_basic_authentication_enabled, ] } } @@ -280,6 +282,39 @@ resource "azurerm_app_service_connection" "test" { `, template, data.RandomString, data.RandomInteger) } +func (r ServiceConnectorAppServiceResource) cosmosdbUpdate(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_app_configuration" "test" { + name = "testacc-appconf%[3]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "free" +} + +resource "azurerm_app_service_connection" "test" { + name = "acctestserviceconnector%[3]d" + app_service_id = azurerm_linux_web_app.test.id + target_resource_id = azurerm_cosmosdb_sql_database.test.id + authentication { + type = "systemAssignedIdentity" + } + scope = "default" + configuration { + action = "enable" + configuration_store { + app_configuration_id = azurerm_app_configuration.test.id + } + } + public_network_solution { + action = "enable" + } +} +`, template, data.RandomString, data.RandomInteger) +} + func (r ServiceConnectorAppServiceResource) secretStore(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -353,6 +388,8 @@ resource "azurerm_linux_web_app" "test" { app_settings["AZURE_STORAGEBLOB_RESOURCEENDPOINT"], identity, sticky_settings, + ftp_publish_basic_authentication_enabled, + webdeploy_publish_basic_authentication_enabled, ] } } @@ -580,6 +617,8 @@ resource "azurerm_linux_web_app" "test" { app_settings, identity, sticky_settings, + ftp_publish_basic_authentication_enabled, + webdeploy_publish_basic_authentication_enabled, ] } } From 6a2354525ef2e895edb7f1b75676fe98b6f000eb Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 19 Sep 2024 14:33:31 +0800 Subject: [PATCH 09/19] add upgrade test --- ...ice_connector_app_service_resource_test.go | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 48d2e82a5136..d029fa0ae8c2 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -50,6 +50,28 @@ func TestAccServiceConnectorAppServiceCosmosdb_basic(t *testing.T) { }) } +func TestAccServiceConnectorAppServiceCosmosdb_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_app_service_connection", "test") + r := ServiceConnectorAppServiceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cosmosdbBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.cosmosdbUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccServiceConnectorAppServiceCosmosdb_secretAuth(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_app_service_connection", "test") r := ServiceConnectorAppServiceResource{} @@ -622,5 +644,5 @@ resource "azurerm_linux_web_app" "test" { ] } } -`, data.RandomInteger, data.Locations.Primary, data.RandomString) +`, data.RandomInteger, "eastus", data.RandomString) } From 345b436eb330f43bc9542c0fdb164e7759176675 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Tue, 8 Oct 2024 14:04:34 +0800 Subject: [PATCH 10/19] use servicelinker to replace links --- internal/services/serviceconnector/helper.go | 27 ++++++++--------- .../service_connector_app_service_resource.go | 1 - ...service_connector_function_app_resource.go | 30 ++++++++++--------- ...service_connector_spring_cloud_resource.go | 28 +++++++++-------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/internal/services/serviceconnector/helper.go b/internal/services/serviceconnector/helper.go index ec44f59a101d..f2abd92d0def 100644 --- a/internal/services/serviceconnector/helper.go +++ b/internal/services/serviceconnector/helper.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" - "github.com/hashicorp/go-azure-sdk/resource-manager/servicelinker/2022-05-01/links" "github.com/hashicorp/go-azure-sdk/resource-manager/servicelinker/2024-04-01/servicelinker" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" @@ -169,7 +168,7 @@ func expandServiceConnectorAuthInfoForCreate(input []AuthInfoModel) (servicelink return nil, fmt.Errorf("unrecognised authentication type: %q", in.Type) } -func expandServiceConnectorAuthInfoForUpdate(input []AuthInfoModel) (links.AuthInfoBase, error) { +func expandServiceConnectorAuthInfoForUpdate(input []AuthInfoModel) (servicelinker.AuthInfoBase, error) { if err := validateServiceConnectorAuthInfo(input); err != nil { return nil, err } @@ -179,34 +178,34 @@ func expandServiceConnectorAuthInfoForUpdate(input []AuthInfoModel) (links.AuthI } in := input[0] - switch links.AuthType(in.Type) { - case links.AuthTypeSecret: - return links.SecretAuthInfo{ + switch servicelinker.AuthType(in.Type) { + case servicelinker.AuthTypeSecret: + return servicelinker.SecretAuthInfo{ Name: pointer.To(in.Name), - SecretInfo: links.ValueSecretInfo{ + SecretInfo: servicelinker.ValueSecretInfo{ Value: pointer.To(in.Secret), }, }, nil - case links.AuthTypeServicePrincipalSecret: - return links.ServicePrincipalSecretAuthInfo{ + case servicelinker.AuthTypeServicePrincipalSecret: + return servicelinker.ServicePrincipalSecretAuthInfo{ ClientId: in.ClientId, PrincipalId: in.PrincipalId, Secret: in.Secret, }, nil - case links.AuthTypeServicePrincipalCertificate: - return links.ServicePrincipalCertificateAuthInfo{ + case servicelinker.AuthTypeServicePrincipalCertificate: + return servicelinker.ServicePrincipalCertificateAuthInfo{ Certificate: in.Certificate, ClientId: in.ClientId, PrincipalId: in.PrincipalId, }, nil - case links.AuthTypeSystemAssignedIdentity: - return links.SystemAssignedIdentityAuthInfo{}, nil + case servicelinker.AuthTypeSystemAssignedIdentity: + return servicelinker.SystemAssignedIdentityAuthInfo{}, nil - case links.AuthTypeUserAssignedIdentity: - return links.UserAssignedIdentityAuthInfo{ + case servicelinker.AuthTypeUserAssignedIdentity: + return servicelinker.UserAssignedIdentityAuthInfo{ ClientId: pointer.To(in.ClientId), SubscriptionId: pointer.To(in.SubscriptionId), }, nil diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index beaad2f466f8..cdb660dbbb43 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -337,7 +337,6 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ServiceConnector.ServiceLinkerClient - id, err := servicelinker.ParseScopedLinkerID(metadata.ResourceData.Id()) if err != nil { return err diff --git a/internal/services/serviceconnector/service_connector_function_app_resource.go b/internal/services/serviceconnector/service_connector_function_app_resource.go index c1ce7a0cee20..bc8bc4652e2e 100644 --- a/internal/services/serviceconnector/service_connector_function_app_resource.go +++ b/internal/services/serviceconnector/service_connector_function_app_resource.go @@ -252,8 +252,8 @@ func (r FunctionAppConnectorResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.ServiceConnector.LinksClient - id, err := links.ParseScopedLinkerID(metadata.ResourceData.Id()) + client := metadata.Client.ServiceConnector.ServiceLinkerClient + id, err := servicelinker.ParseScopedLinkerID(metadata.ResourceData.Id()) if err != nil { return err } @@ -263,24 +263,26 @@ func (r FunctionAppConnectorResource) Update() sdk.ResourceFunc { return fmt.Errorf("decoding state: %+v", err) } - linkerProps := links.LinkerProperties{} + existing, err := client.LinkerGet(ctx, *id) + if err != nil { + return fmt.Errorf("checking for presence of existing %s: %+v", *id, err) + } + linkerProps := existing.Model.Properties d := metadata.ResourceData if d.HasChange("client_type") { - clientType := links.ClientType(state.ClientType) - linkerProps.ClientType = &clientType + linkerProps.ClientType = pointer.To(servicelinker.ClientType(state.ClientType)) } if d.HasChange("vnet_solution") { - vnetSolutionType := links.VNetSolutionType(state.VnetSolution) - vnetSolution := links.VNetSolution{ - Type: &vnetSolutionType, + vnetSolution := servicelinker.VNetSolution{ + Type: pointer.To(servicelinker.VNetSolutionType(state.VnetSolution)), } - linkerProps.VNetSolution = &vnetSolution + linkerProps.VNetSolution = pointer.To(vnetSolution) } if d.HasChange("secret_store") { - linkerProps.SecretStore = pointer.To(links.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) + linkerProps.SecretStore = pointer.To(servicelinker.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) } if d.HasChange("authentication") { @@ -292,12 +294,12 @@ func (r FunctionAppConnectorResource) Update() sdk.ResourceFunc { linkerProps.AuthInfo = authInfo } - props := links.LinkerPatch{ - Properties: &linkerProps, + props := servicelinker.LinkerResource{ + Properties: linkerProps, } - if err := client.LinkerUpdateThenPoll(ctx, *id, props); err != nil { - return fmt.Errorf("updating %s: %+v", id, err) + if err := client.LinkerCreateOrUpdateThenPoll(ctx, *id, props); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } return nil diff --git a/internal/services/serviceconnector/service_connector_spring_cloud_resource.go b/internal/services/serviceconnector/service_connector_spring_cloud_resource.go index 13c0e36016cb..016982e72df7 100644 --- a/internal/services/serviceconnector/service_connector_spring_cloud_resource.go +++ b/internal/services/serviceconnector/service_connector_spring_cloud_resource.go @@ -255,8 +255,8 @@ func (r SpringCloudConnectorResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.ServiceConnector.LinksClient - id, err := links.ParseScopedLinkerID(metadata.ResourceData.Id()) + client := metadata.Client.ServiceConnector.ServiceLinkerClient + id, err := servicelinker.ParseScopedLinkerID(metadata.ResourceData.Id()) if err != nil { return err } @@ -266,24 +266,26 @@ func (r SpringCloudConnectorResource) Update() sdk.ResourceFunc { return fmt.Errorf("decoding %+v", err) } - linkerProps := links.LinkerProperties{} + existing, err := client.LinkerGet(ctx, *id) + if err != nil { + return fmt.Errorf("checking for presence of existing %s: %+v", *id, err) + } + linkerProps := existing.Model.Properties d := metadata.ResourceData if d.HasChange("client_type") { - clientType := links.ClientType(state.ClientType) - linkerProps.ClientType = &clientType + linkerProps.ClientType = pointer.To(servicelinker.ClientType(state.ClientType)) } if d.HasChange("vnet_solution") { - vnetSolutionType := links.VNetSolutionType(state.VnetSolution) - vnetSolution := links.VNetSolution{ - Type: &vnetSolutionType, + vnetSolution := servicelinker.VNetSolution{ + Type: pointer.To(servicelinker.VNetSolutionType(state.VnetSolution)), } - linkerProps.VNetSolution = &vnetSolution + linkerProps.VNetSolution = pointer.To(vnetSolution) } if d.HasChange("secret_store") { - linkerProps.SecretStore = pointer.To(links.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) + linkerProps.SecretStore = pointer.To(servicelinker.SecretStore{KeyVaultId: expandSecretStore(state.SecretStore).KeyVaultId}) } if d.HasChange("authentication") { @@ -295,11 +297,11 @@ func (r SpringCloudConnectorResource) Update() sdk.ResourceFunc { linkerProps.AuthInfo = authInfo } - props := links.LinkerPatch{ - Properties: &linkerProps, + props := servicelinker.LinkerResource{ + Properties: linkerProps, } - if err := client.LinkerUpdateThenPoll(ctx, *id, props); err != nil { + if err := client.LinkerCreateOrUpdateThenPoll(ctx, *id, props); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } return nil From 8cc441f0ff21985dd0a4f295ec17f9ebc1c3866e Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Tue, 29 Oct 2024 17:08:21 +0800 Subject: [PATCH 11/19] remove hard coded location in tests --- .../service_connector_app_service_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index d029fa0ae8c2..e6686d4c92a0 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -644,5 +644,5 @@ resource "azurerm_linux_web_app" "test" { ] } } -`, data.RandomInteger, "eastus", data.RandomString) +`, data.RandomInteger, data.Locations.Primary, data.RandomString) } From b1153d3f997186669cb1a4281fa6438a63e1f7e7 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Wed, 30 Oct 2024 14:24:18 +0800 Subject: [PATCH 12/19] fix target service unaligned issue in update --- .../serviceconnector/service_connector_app_service_resource.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index cdb660dbbb43..cf9aef9171ea 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -354,6 +354,9 @@ func (r AppServiceConnectorResource) Update() sdk.ResourceFunc { linkerProps := existing.Model.Properties d := metadata.ResourceData + linkerProps.TargetService = servicelinker.AzureResource{ + Id: pointer.To(state.TargetResourceId), + } if d.HasChange("client_type") { linkerProps.ClientType = pointer.To(servicelinker.ClientType(state.ClientType)) } From 137a29652f5af695f57b10396cef5077fc96682f Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 21 Nov 2024 09:55:19 +0800 Subject: [PATCH 13/19] add validation func for scope --- .../service_connector_app_service_resource.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource.go b/internal/services/serviceconnector/service_connector_app_service_resource.go index cf9aef9171ea..7fa0a6b34f11 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource.go @@ -94,8 +94,9 @@ func (r AppServiceConnectorResource) Arguments() map[string]*schema.Schema { "authentication": authInfoSchema(), "scope": { - Type: pluginsdk.TypeString, - Optional: true, + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, }, "configuration": { From f8f003c05092f58fca81465826f18d284e1fb137 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Wed, 4 Dec 2024 10:40:42 +0800 Subject: [PATCH 14/19] address comments on docs --- website/docs/r/app_service_connection.html.markdown | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/website/docs/r/app_service_connection.html.markdown b/website/docs/r/app_service_connection.html.markdown index bdd3b4093a5a..c29c694bc3a7 100644 --- a/website/docs/r/app_service_connection.html.markdown +++ b/website/docs/r/app_service_connection.html.markdown @@ -112,9 +112,9 @@ An `authentication` block supports the following: --- -* `scope` - (Optional) Connection scope in source service. It's namespace name if source is AKS cluster, container name if source is Container app. +* `scope` - (Optional) Connection scope in the source service. This is the Namespace Name if the source is an AKS cluster, or the Container Name if source is a Container App. -* `configuration` - (Optional) The connection information consumed by applications, including secrets, connection strings. A `configuration` block as defined below. +* `configuration` - (Optional) The connection information consumed by applications, including secrets and connection strings. A `configuration` block as defined below. --- @@ -152,6 +152,12 @@ An `secret_store` block supports the following: * `key_vault_id` - (Required) The key vault id to store secret. +--- + +An `configuration_store` block supports the following: + +* `app_configuration_id` - (Optional) The app configuration id to store configuration. + ## Attribute Reference In addition to the Arguments listed above - the following Attributes are exported: From 9e0f6b1e0cd061481fc3d71573d7f93c569ec61e Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 5 Dec 2024 10:04:21 +0800 Subject: [PATCH 15/19] fix acc test --- .../service_connector_app_service_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index e6686d4c92a0..319dd3b4c773 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -568,7 +568,7 @@ resource "azurerm_app_service_connection" "test" { } } public_network_solution { - action = "enable" + action = "optOut" } } `, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) From 115b863c8e48c582bc0ff74818f0a6ea3caa5971 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 5 Dec 2024 11:16:15 +0800 Subject: [PATCH 16/19] remove network solution in acc test --- .../service_connector_app_service_resource_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 319dd3b4c773..4ccd08bf76c2 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -567,9 +567,6 @@ resource "azurerm_app_service_connection" "test" { app_configuration_id = azurerm_app_configuration.test.id } } - public_network_solution { - action = "optOut" - } } `, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) } From a84ac6536583e5b28ef3af4393237d7e92e7853d Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Thu, 5 Dec 2024 16:21:07 +0800 Subject: [PATCH 17/19] fix subnet upgrade issue --- .../service_connector_app_service_resource_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index 4ccd08bf76c2..bd3eebcd9f0e 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -507,6 +507,7 @@ resource "azurerm_subnet" "test1" { resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] + private_endpoint_network_policies = "Enabled" delegation { name = "delegation" From 77bca43e3c6f964d7f2e90dfb01b90e0e42d8444 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Tue, 10 Dec 2024 17:19:34 +0800 Subject: [PATCH 18/19] Fix complete acc test --- .../service_connector_app_service_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index bd3eebcd9f0e..be9900d2044c 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -557,7 +557,7 @@ resource "azurerm_app_service_connection" "test" { app_service_id = azurerm_linux_web_app.test.id target_resource_id = azurerm_cosmosdb_sql_database.test.id client_type = "java" - vnet_solution = "serviceEndpoint" + authentication { type = "systemAssignedIdentity" } From 9cfbc5cacc842b73bcdece0408abbb1d104d2071 Mon Sep 17 00:00:00 2001 From: Jiawei Tao Date: Mon, 16 Dec 2024 11:38:10 +0800 Subject: [PATCH 19/19] Fix indent --- .../service_connector_app_service_resource_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/services/serviceconnector/service_connector_app_service_resource_test.go b/internal/services/serviceconnector/service_connector_app_service_resource_test.go index be9900d2044c..991e710bdedd 100644 --- a/internal/services/serviceconnector/service_connector_app_service_resource_test.go +++ b/internal/services/serviceconnector/service_connector_app_service_resource_test.go @@ -503,10 +503,10 @@ resource "azurerm_virtual_network" "test" { } resource "azurerm_subnet" "test1" { - name = "subnet1" - resource_group_name = azurerm_resource_group.test.name - virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.0.1.0/24"] + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] private_endpoint_network_policies = "Enabled" delegation {