From 520f6c7c27d9d22cd10e37c748e86441afaf4ac7 Mon Sep 17 00:00:00 2001 From: bubbletroubles <42738824+bubbletroubles@users.noreply.github.com> Date: Fri, 15 Jul 2022 08:44:56 +1000 Subject: [PATCH] Updated to remove ForceNew (#4) --- .../appservice/linux_function_app_resource.go | 20 ++- .../linux_function_app_resource_test.go | 125 ++++++++++++++++-- .../linux_function_app_slot_resource.go | 20 ++- .../linux_function_app_slot_resource_test.go | 120 +++++++++++++++-- .../docs/r/linux_function_app.html.markdown | 2 +- .../r/linux_function_app_slot.html.markdown | 2 +- 6 files changed, 261 insertions(+), 28 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index b26bfd330ca1..3712827bc625 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -246,9 +247,9 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "tags": tags.Schema(), "virtual_network_subnet_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, }, } } @@ -717,6 +718,19 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { existing.SiteProperties.HTTPSOnly = utils.Bool(state.HttpsOnly) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetwork(ctx, id.ResourceGroup, id.SiteName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + if metadata.ResourceData.HasChange("client_certificate_enabled") { existing.SiteProperties.ClientCertEnabled = utils.Bool(state.ClientCertEnabled) } diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 5c8e5ea19958..bf176991e139 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -1199,11 +1199,11 @@ func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_withSubnetId(data, SkuStandardPlan), + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( - check.That("azurerm_subnet.test").Key("id"), + check.That("azurerm_subnet.test1").Key("id"), ), ), }, @@ -1224,11 +1224,21 @@ func TestAccLinuxFunctionApp_vNetIntegrationUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.vNetIntegration_withSubnetId(data, SkuStandardPlan), + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( - check.That("azurerm_subnet.test").Key("id"), + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), ), ), }, @@ -3202,8 +3212,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +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"] @@ -3218,6 +3228,22 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_function_app" "test" { name = "acctest-LFA-%d" location = azurerm_resource_group.test.location @@ -3235,7 +3261,7 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } -func (r LinuxFunctionAppResource) vNetIntegration_withSubnetId(data acceptance.TestData, planSku string) string { +func (r LinuxFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3250,8 +3276,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +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"] @@ -3266,12 +3292,91 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + virtual_network_subnet_id = azurerm_subnet.test1.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + site_config {} + +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_function_app" "test" { name = "acctest-LFA-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name service_plan_id = azurerm_service_plan.test.id - virtual_network_subnet_id = azurerm_subnet.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 94fde0053e60..752cd680b749 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate" kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -227,9 +228,9 @@ func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { "tags": tags.Schema(), "virtual_network_subnet_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: networkValidate.SubnetID, }, } } @@ -711,6 +712,19 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("virtual_network_subnet_id") { + subnetId := metadata.ResourceData.Get("virtual_network_subnet_id").(string) + if subnetId == "" { + if _, err := client.DeleteSwiftVirtualNetworkSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName); err != nil { + return fmt.Errorf("removing `virtual_network_subnet_id` association for %s: %+v", *id, err) + } + var empty *string + existing.SiteProperties.VirtualNetworkSubnetID = empty + } else { + existing.SiteProperties.VirtualNetworkSubnetID = utils.String(subnetId) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { diff --git a/internal/services/appservice/linux_function_app_slot_resource_test.go b/internal/services/appservice/linux_function_app_slot_resource_test.go index e37d7bd2d36c..2cb2197807f2 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -947,11 +947,11 @@ func TestAccLinuxFunctionAppSlot_vNetIntegration(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_withSubnetId(data, SkuStandardPlan), + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( - check.That("azurerm_subnet.test").Key("id"), + check.That("azurerm_subnet.test1").Key("id"), ), ), }, @@ -972,11 +972,21 @@ func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.vNetIntegration_withSubnetId(data, SkuStandardPlan), + Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( - check.That("azurerm_subnet.test").Key("id"), + check.That("azurerm_subnet.test1").Key("id"), + ), + ), + }, + data.ImportStep(), + { + Config: r.vNetIntegration_subnet2(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( + check.That("azurerm_subnet.test2").Key("id"), ), ), }, @@ -2587,8 +2597,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +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"] @@ -2603,6 +2613,22 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_function_app_slot" "test" { name = "acctest-LFAS-%d" function_app_id = azurerm_linux_function_app.test.id @@ -2612,11 +2638,69 @@ resource "azurerm_linux_function_app_slot" "test" { site_config {} } +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_virtual_network" "test" { + name = "vnet-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%d" + function_app_id = azurerm_linux_function_app.test.id + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + virtual_network_subnet_id = azurerm_subnet.test1.id + + site_config {} +} `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } -func (r LinuxFunctionAppSlotResource) vNetIntegration_withSubnetId(data acceptance.TestData, planSku string) string { +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2631,8 +2715,8 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -resource "azurerm_subnet" "test" { - name = "subnet" +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"] @@ -2647,12 +2731,28 @@ resource "azurerm_subnet" "test" { } } +resource "azurerm_subnet" "test2" { + name = "subnet2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + resource "azurerm_linux_function_app_slot" "test" { name = "acctest-LFAS-%d" function_app_id = azurerm_linux_function_app.test.id storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - virtual_network_subnet_id = azurerm_subnet.test.id + virtual_network_subnet_id = azurerm_subnet.test2.id site_config {} } diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index 361d612da714..2dfab552decb 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -112,7 +112,7 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags which should be assigned to the Linux Function App. -* `virtual_network_subnet_id` - (Optional) The subnet id which will be used by this Function App for [regional virtual network integration](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration). Changing this forces a new Linux Function App to be created. +* `virtual_network_subnet_id` - (Optional) The subnet id which will be used by this Function App for [regional virtual network integration](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration). ~> **NOTE on regional virtual network integration:** The AzureRM Terraform provider provides regional virtual network integration via the standalone resource [app_service_virtual_network_swift_connection](app_service_virtual_network_swift_connection.html) and in-line within this resource using the `virtual_network_subnet_id` property. You cannot use both methods simutaneously. diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index 7fafdf04ca2b..e715fb05d347 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -114,7 +114,7 @@ The following arguments are supported: * `tags` - (Optional) A mapping of tags which should be assigned to the Linux Function App. -* `virtual_network_subnet_id` - (Optional) The subnet id which will be used by this Function App Slot for [regional virtual network integration](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration). Changing this forces a new Linux Function App Slot to be created. +* `virtual_network_subnet_id` - (Optional) The subnet id which will be used by this Function App Slot for [regional virtual network integration](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#regional-virtual-network-integration). ~> **NOTE on regional virtual network integration:** The AzureRM Terraform provider provides regional virtual network integration via the standalone resource [app_service_virtual_network_swift_connection](app_service_virtual_network_swift_connection.html) and in-line within this resource using the `virtual_network_subnet_id` property. You cannot use both methods simutaneously.