From 5bf320aaa7e5e7246d329658159f829391c0f6c1 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Thu, 14 Mar 2024 15:18:43 +0800 Subject: [PATCH 01/16] adding vnetImagePullEnabled property to linux function app --- .../appservice/linux_function_app_resource.go | 14 ++++++++++++++ .../appservice/linux_function_app_resource_test.go | 4 ++++ .../appservice/linux_function_app_slot_resource.go | 14 ++++++++++++++ .../linux_function_app_slot_resource_test.go | 11 +++++++++++ website/docs/r/linux_function_app.html.markdown | 4 ++++ .../docs/r/linux_function_app_slot.html.markdown | 6 +++++- 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 788943a26414..9f5aff6ba294 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -66,6 +66,7 @@ type LinuxFunctionAppModel struct { StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` + VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans ZipDeployFile string `tfschema:"zip_deploy_file"` PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` @@ -297,6 +298,13 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { ValidateFunc: commonids.ValidateSubnetID, }, + "vnet_image_pull_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + }, + "zip_deploy_file": { Type: pluginsdk.TypeString, Optional: true, @@ -523,6 +531,7 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), // TODO - Investigate, setting appears silently ignored on Linux Function Apps? VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, + VnetImagePullEnabled: pointer.To(functionApp.VnetImagePullEnabled), }, } @@ -763,6 +772,7 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) + state.VnetImagePullEnabled = pointer.From(props.VnetImagePullEnabled) servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmId) if err != nil { @@ -929,6 +939,10 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") { + model.Properties.VnetImagePullEnabled = pointer.To(state.VnetImagePullEnabled) + } + if metadata.ResourceData.HasChange("client_certificate_enabled") { model.Properties.ClientCertEnabled = pointer.To(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 50fc0c932a6e..5dc2522baa0c 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -3315,6 +3315,7 @@ resource "azurerm_linux_function_app" "test" { ftp_publish_basic_authentication_enabled = false webdeploy_publish_basic_authentication_enabled = false + vnet_image_pull_enabled = true tags = { terraform = "true" @@ -3462,6 +3463,8 @@ resource "azurerm_linux_function_app" "test" { vnet_route_all_enabled = true } + vnet_image_pull_enabled = true + } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } @@ -4154,6 +4157,7 @@ resource "azurerm_linux_function_app" "test" { service_plan_id = azurerm_service_plan.test.id virtual_network_subnet_id = azurerm_subnet.test1.id + vnet_image_pull_enabled = true 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 8076560b8272..57c02c2b5f01 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -59,6 +59,7 @@ type LinuxFunctionAppSlotModel struct { SiteConfig []helpers.SiteConfigLinuxFunctionAppSlot `tfschema:"site_config"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` + VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` HostingEnvId string `tfschema:"hosting_environment_id"` DefaultHostname string `tfschema:"default_hostname"` @@ -280,6 +281,13 @@ func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { Optional: true, ValidateFunc: commonids.ValidateSubnetID, }, + + "vnet_image_pull_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + }, } } @@ -517,6 +525,7 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { ClientCertMode: pointer.To(webapps.ClientCertMode(functionAppSlot.ClientCertMode)), DailyMemoryTimeQuota: pointer.To(functionAppSlot.DailyMemoryTimeQuota), VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, // (@jackofallops) - Value appear to need to be set in both SiteProperties and SiteConfig for now? https://github.com/Azure/azure-rest-api-specs/issues/24681 + VnetImagePullEnabled: pointer.To(functionAppSlot.VnetImagePullEnabled), }, } @@ -735,6 +744,7 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) + state.VnetImagePullEnabled = pointer.From(props.VnetImagePullEnabled) if hostingEnv := props.HostingEnvironmentProfile; hostingEnv != nil { state.HostingEnvId = pointer.From(hostingEnv.Id) @@ -923,6 +933,10 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") { + model.Properties.VnetImagePullEnabled = pointer.To(state.VnetImagePullEnabled) + } + 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 4b0255238583..c0ee365c3fe1 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -2461,6 +2461,8 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } + vnet_image_pull_enabled = true + tags = { terraform = "true" Env = "AccTest" @@ -2502,6 +2504,9 @@ resource "azurerm_linux_function_app_slot" "test" { virtual_network_subnet_id = azurerm_subnet.test.id } } + + vnet_image_pull_enabled = true + } `, r.template(data, planSku), data.RandomInteger) } @@ -2640,6 +2645,7 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } + vnet_image_pull_enabled = true } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } @@ -3226,6 +3232,8 @@ resource "azurerm_linux_function_app_slot" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} + + vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) @@ -3286,6 +3294,9 @@ resource "azurerm_linux_function_app_slot" "test" { virtual_network_subnet_id = azurerm_subnet.test1.id site_config {} + + vnet_image_pull_enabled = true + } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index 464b0e362a04..0d6a3e19b0b5 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -139,6 +139,10 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) +* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`. + +~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. + * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. ~> **NOTE:** Setting this value to true will disable the ability to use `zip_deploy_file` which currently relies on the default publishing profile. diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index 2726d53ceb16..7b26652a970d 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -131,7 +131,11 @@ The following arguments are supported: ~> **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 simultaneously. If the virtual network is set via the resource `app_service_virtual_network_swift_connection` then `ignore_changes` should be used in the function app slot configuration. ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) - + +* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`. + +~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. + * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. --- From 4fa4d9680a971da50a0521dd577dcfc37af236af Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Mon, 18 Mar 2024 16:44:26 +0800 Subject: [PATCH 02/16] change the default value for ase --- .../appservice/linux_function_app_resource.go | 20 +++++++++++++++++++ .../linux_function_app_resource_test.go | 2 +- .../linux_function_app_slot_resource_test.go | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 9f5aff6ba294..e3b4ff333a84 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -402,6 +402,7 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { } var planSKU *string + var isAseEnvironment bool availabilityRequest := resourceproviders.ResourceNameAvailabilityRequest{ Name: functionApp.Name, Type: resourceproviders.CheckNameResourceTypesMicrosoftPointWebSites, @@ -412,6 +413,7 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { } if ase := servicePlanModel.Properties.HostingEnvironmentProfile; ase != nil { + isAseEnvironment = true // Attempt to check the ASE for the appropriate suffix for the name availability request. // This varies between internal and external ASE Types, and potentially has other names in other clouds // We use the "internal" as the fallback here, if we can read the ASE, we'll get the full one @@ -535,6 +537,10 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { }, } + if isAseEnvironment && !functionApp.VnetImagePullEnabled { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + pna := helpers.PublicNetworkAccessEnabled if !functionApp.PublicNetworkAccess { pna = helpers.PublicNetworkAccessDisabled @@ -1224,6 +1230,20 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AppService.ServicePlanClient rd := metadata.ResourceDiff + if rd.HasChange("vnet_image_pull_enabled") { + _, newValue := rd.GetChange("vnet_image_pull_enabled") + servicePlanId, err := commonids.ParseAppServicePlanID(rd.Get("service_plan_id").(string)) + asp, err := client.Get(ctx, *servicePlanId) + if err != nil { + return fmt.Errorf("reading %s: %+v", servicePlanId, err) + } + if aspModel := asp.Model; aspModel != nil { + if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && + aspModel.Properties.HostingEnvironmentProfile.Id != nil && !newValue.(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } + } if rd.HasChange("service_plan_id") { currentPlanIdRaw, newPlanIdRaw := rd.GetChange("service_plan_id") if newPlanIdRaw.(string) == "" { diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 5dc2522baa0c..0cc939bbb6c4 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -3315,7 +3315,6 @@ resource "azurerm_linux_function_app" "test" { ftp_publish_basic_authentication_enabled = false webdeploy_publish_basic_authentication_enabled = false - vnet_image_pull_enabled = true tags = { terraform = "true" @@ -4248,6 +4247,7 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } 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 c0ee365c3fe1..9ed7296d14d7 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -3381,6 +3381,8 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + vnet_image_pull_enabled = true + site_config { vnet_route_all_enabled = true } From 206124e680a4d0063728029017f2377c337459cf Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Mon, 18 Mar 2024 17:10:09 +0800 Subject: [PATCH 03/16] fix error --- internal/services/appservice/linux_function_app_resource.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index e3b4ff333a84..776a3a81776f 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -1233,9 +1233,13 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { if rd.HasChange("vnet_image_pull_enabled") { _, newValue := rd.GetChange("vnet_image_pull_enabled") servicePlanId, err := commonids.ParseAppServicePlanID(rd.Get("service_plan_id").(string)) + if err != nil { + return fmt.Errorf("reading service plan id %+v", err) + } + asp, err := client.Get(ctx, *servicePlanId) if err != nil { - return fmt.Errorf("reading %s: %+v", servicePlanId, err) + return fmt.Errorf("could not read Service Plan %s: %+v", servicePlanId, err) } if aspModel := asp.Model; aspModel != nil { if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && From 4f7253b4b7b0b12b860d5de170b6fd00680798e3 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Mon, 18 Mar 2024 19:29:40 +0800 Subject: [PATCH 04/16] update customize diff func --- internal/services/appservice/linux_function_app_resource.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 776a3a81776f..2a36c329cfa2 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -1231,8 +1231,9 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { client := metadata.Client.AppService.ServicePlanClient rd := metadata.ResourceDiff if rd.HasChange("vnet_image_pull_enabled") { + planId := rd.Get("service_plan_id") _, newValue := rd.GetChange("vnet_image_pull_enabled") - servicePlanId, err := commonids.ParseAppServicePlanID(rd.Get("service_plan_id").(string)) + servicePlanId, err := commonids.ParseAppServicePlanID(planId.(string)) if err != nil { return fmt.Errorf("reading service plan id %+v", err) } From 62d0f524a9b1395a0cfe0be0bcd2500a2dcc2258 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Tue, 19 Mar 2024 15:12:45 +0800 Subject: [PATCH 05/16] update for ase --- internal/services/appservice/linux_function_app_resource.go | 4 ++++ website/docs/r/linux_function_app.html.markdown | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 2a36c329cfa2..245735f8ceee 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -1232,6 +1232,10 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { rd := metadata.ResourceDiff if rd.HasChange("vnet_image_pull_enabled") { planId := rd.Get("service_plan_id") + // the plan id is known after apply during the initial creation + if planId.(string) == "" { + return nil + } _, newValue := rd.GetChange("vnet_image_pull_enabled") servicePlanId, err := commonids.ParseAppServicePlanID(planId.(string)) if err != nil { diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index 0d6a3e19b0b5..c65dd1130926 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -141,7 +141,7 @@ The following arguments are supported: * `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`. -~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. +~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment. * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. From 585746da5b7b17bfa2897e3976341cbeeadfffda Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Wed, 3 Apr 2024 10:01:19 +0800 Subject: [PATCH 06/16] update test cases to remove the vnetimagepull property --- .../appservice/linux_function_app_resource.go | 43 +++++++++++-------- .../linux_function_app_resource_test.go | 6 +-- .../linux_function_app_slot_resource.go | 25 +++++++---- .../linux_function_app_slot_resource_test.go | 12 +++--- .../docs/r/linux_function_app.html.markdown | 3 +- .../r/linux_function_app_slot.html.markdown | 3 +- 6 files changed, 54 insertions(+), 38 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 4d9fcaca94b3..c05862dfe1f9 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -6,6 +6,7 @@ package appservice import ( "context" "fmt" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -66,12 +67,13 @@ type LinuxFunctionAppModel struct { StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` - VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans ZipDeployFile string `tfschema:"zip_deploy_file"` PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` DefaultHostname string `tfschema:"default_hostname"` @@ -298,13 +300,6 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { ValidateFunc: commonids.ValidateSubnetID, }, - "vnet_image_pull_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - Description: "Is container image pull over virtual network enabled? Defaults to `false`.", - }, - "zip_deploy_file": { Type: pluginsdk.TypeString, Optional: true, @@ -316,7 +311,7 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { } func (r LinuxFunctionAppResource) Attributes() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + s := map[string]*pluginsdk.Schema{ "custom_domain_verification_id": { Type: pluginsdk.TypeString, Computed: true, @@ -366,6 +361,15 @@ func (r LinuxFunctionAppResource) Attributes() map[string]*pluginsdk.Schema { "site_credential": helpers.SiteCredentialSchema(), } + if features.FourPointOhBeta() { + s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + } + } + return s } func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { @@ -402,7 +406,6 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { } var planSKU *string - var isAseEnvironment bool availabilityRequest := resourceproviders.ResourceNameAvailabilityRequest{ Name: functionApp.Name, Type: resourceproviders.CheckNameResourceTypesMicrosoftPointWebSites, @@ -413,7 +416,6 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { } if ase := servicePlanModel.Properties.HostingEnvironmentProfile; ase != nil { - isAseEnvironment = true // Attempt to check the ASE for the appropriate suffix for the name availability request. // This varies between internal and external ASE Types, and potentially has other names in other clouds // We use the "internal" as the fallback here, if we can read the ASE, we'll get the full one @@ -435,6 +437,11 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { availabilityRequest.Name = fmt.Sprintf("%s.%s", functionApp.Name, nameSuffix) availabilityRequest.IsFqdn = pointer.To(true) + if features.FourPointOhBeta() { + if !metadata.ResourceData.Get("vnet_image_pull_enabled").(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } } } // Only send for ElasticPremium and Consumption plan @@ -533,12 +540,10 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), // TODO - Investigate, setting appears silently ignored on Linux Function Apps? VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, - VnetImagePullEnabled: pointer.To(functionApp.VnetImagePullEnabled), }, } - - if isAseEnvironment && !functionApp.VnetImagePullEnabled { - return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + if features.FourPointOhBeta() { + siteEnvelope.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) } pna := helpers.PublicNetworkAccessEnabled @@ -778,8 +783,10 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) - state.VnetImagePullEnabled = pointer.From(props.VnetImagePullEnabled) + if features.FourPointOhBeta() { + metadata.ResourceData.Set("vnet_image_pull_enabled", pointer.From(props.VnetImagePullEnabled)) + } servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmId) if err != nil { return err @@ -945,8 +952,8 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { } } - if metadata.ResourceData.HasChange("vnet_image_pull_enabled") { - model.Properties.VnetImagePullEnabled = pointer.To(state.VnetImagePullEnabled) + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { + model.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) } if metadata.ResourceData.HasChange("client_certificate_enabled") { diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 0cc939bbb6c4..c9be61e07f5a 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -3462,7 +3462,7 @@ resource "azurerm_linux_function_app" "test" { vnet_route_all_enabled = true } - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) @@ -4156,7 +4156,7 @@ resource "azurerm_linux_function_app" "test" { service_plan_id = azurerm_service_plan.test.id virtual_network_subnet_id = azurerm_subnet.test1.id - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key @@ -4247,7 +4247,7 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 5b3d144412c8..c4a34e30f3ba 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -6,6 +6,7 @@ package appservice import ( "context" "fmt" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -59,7 +60,6 @@ type LinuxFunctionAppSlotModel struct { SiteConfig []helpers.SiteConfigLinuxFunctionAppSlot `tfschema:"site_config"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` - VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` HostingEnvId string `tfschema:"hosting_environment_id"` DefaultHostname string `tfschema:"default_hostname"` @@ -93,7 +93,7 @@ func (r LinuxFunctionAppSlotResource) IDValidationFunc() pluginsdk.SchemaValidat } func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + s := map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, @@ -281,14 +281,16 @@ func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { Optional: true, ValidateFunc: commonids.ValidateSubnetID, }, - - "vnet_image_pull_enabled": { + } + if features.FourPointOhBeta() { + s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ Type: pluginsdk.TypeBool, Optional: true, Default: false, Description: "Is container image pull over virtual network enabled? Defaults to `false`.", - }, + } } + return s } func (r LinuxFunctionAppSlotResource) Attributes() map[string]*pluginsdk.Schema { @@ -525,10 +527,13 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { ClientCertMode: pointer.To(webapps.ClientCertMode(functionAppSlot.ClientCertMode)), DailyMemoryTimeQuota: pointer.To(functionAppSlot.DailyMemoryTimeQuota), VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, // (@jackofallops) - Value appear to need to be set in both SiteProperties and SiteConfig for now? https://github.com/Azure/azure-rest-api-specs/issues/24681 - VnetImagePullEnabled: pointer.To(functionAppSlot.VnetImagePullEnabled), }, } + if features.FourPointOhBeta() { + siteEnvelope.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) + } + pan := helpers.PublicNetworkAccessEnabled if !functionAppSlot.PublicNetworkAccess { pan = helpers.PublicNetworkAccessDisabled @@ -744,8 +749,10 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) - state.VnetImagePullEnabled = pointer.From(props.VnetImagePullEnabled) + if features.FourPointOhBeta() { + metadata.ResourceData.Set("vnet_image_pull_enabled", pointer.From(props.VnetImagePullEnabled)) + } if hostingEnv := props.HostingEnvironmentProfile; hostingEnv != nil { state.HostingEnvId = pointer.From(hostingEnv.Id) } @@ -933,8 +940,8 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { } } - if metadata.ResourceData.HasChange("vnet_image_pull_enabled") { - model.Properties.VnetImagePullEnabled = pointer.To(state.VnetImagePullEnabled) + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { + model.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) } storageString := state.StorageAccountName 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 9ed7296d14d7..2227e3dd0c73 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -2461,7 +2461,7 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true tags = { terraform = "true" @@ -2505,7 +2505,7 @@ resource "azurerm_linux_function_app_slot" "test" { } } - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger) @@ -2645,7 +2645,7 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } @@ -3233,7 +3233,7 @@ resource "azurerm_linux_function_app_slot" "test" { site_config {} - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) @@ -3295,7 +3295,7 @@ resource "azurerm_linux_function_app_slot" "test" { site_config {} - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) @@ -3381,7 +3381,7 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - vnet_image_pull_enabled = true + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index c65dd1130926..198a7a1bff97 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -139,7 +139,8 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`. +[//]: # (todo: add in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) ~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment. diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index 7b26652a970d..f1f5cb8789e5 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -132,7 +132,8 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`. +[//]: # (todo: add in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) ~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. From b3a8079ffa6dc1d9a7028ca2d0cbcd6e0ec293e5 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Wed, 3 Apr 2024 13:38:58 +0800 Subject: [PATCH 07/16] add 4.0 flag and windows function apps --- .../appservice/linux_function_app_resource.go | 24 ++++---- .../linux_function_app_slot_resource.go | 38 +++++++++++++ .../windows_function_app_resource.go | 54 +++++++++++++++++- .../windows_function_app_slot_resource.go | 57 ++++++++++++++++++- .../docs/r/linux_function_app.html.markdown | 2 +- .../docs/r/windows_function_app.html.markdown | 3 + .../r/windows_function_app_slot.html.markdown | 3 + 7 files changed, 166 insertions(+), 15 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index c05862dfe1f9..e038ebdd1518 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -108,7 +108,7 @@ func (r LinuxFunctionAppResource) IDValidationFunc() pluginsdk.SchemaValidateFun } func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + s := map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, @@ -308,10 +308,19 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { Description: "The local path and filename of the Zip packaged application to deploy to this Linux Function App. **Note:** Using this value requires either `WEBSITE_RUN_FROM_PACKAGE=1` or `SCM_DO_BUILD_DURING_DEPLOYMENT=true` to be set on the App in `app_settings`.", }, } + if features.FourPointOhBeta() { + s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + } + } + return s } func (r LinuxFunctionAppResource) Attributes() map[string]*pluginsdk.Schema { - s := map[string]*pluginsdk.Schema{ + return map[string]*pluginsdk.Schema{ "custom_domain_verification_id": { Type: pluginsdk.TypeString, Computed: true, @@ -361,15 +370,6 @@ func (r LinuxFunctionAppResource) Attributes() map[string]*pluginsdk.Schema { "site_credential": helpers.SiteCredentialSchema(), } - if features.FourPointOhBeta() { - s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - Description: "Is container image pull over virtual network enabled? Defaults to `false`.", - } - } - return s } func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { @@ -1255,7 +1255,7 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { } if aspModel := asp.Model; aspModel != nil { if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && - aspModel.Properties.HostingEnvironmentProfile.Id != nil && !newValue.(bool) { + aspModel.Properties.HostingEnvironmentProfile.Id != nil && *(aspModel.Properties.HostingEnvironmentProfile.Id) != "" && !newValue.(bool) { return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") } } diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index c4a34e30f3ba..7f8186fd0299 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -74,6 +74,7 @@ type LinuxFunctionAppSlotModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider } var _ sdk.ResourceWithUpdate = LinuxFunctionAppSlotResource{} @@ -437,6 +438,11 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { availabilityRequest.Name = fmt.Sprintf("%s.%s", functionAppSlot.Name, nameSuffix) availabilityRequest.IsFqdn = pointer.To(true) + if features.FourPointOhBeta() { + if !metadata.ResourceData.Get("vnet_image_pull_enabled").(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } } } @@ -1207,3 +1213,35 @@ func (r LinuxFunctionAppSlotResource) StateUpgraders() sdk.StateUpgradeData { }, } } + +func (r LinuxFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + appClient := metadata.Client.AppService.WebAppsClient + rd := metadata.ResourceDiff + if rd.HasChange("vnet_image_pull_enabled") { + appId := rd.Get("function_app_id") + if appId.(string) == "" { + return nil + } + _, newValue := rd.GetChange("vnet_image_pull_enabled") + functionAppId, err := commonids.ParseAppServiceID(appId.(string)) + if err != nil { + return fmt.Errorf("reading Function App %+v", err) + } + + functionApp, err := appClient.Get(ctx, *functionAppId) + if err != nil { + return fmt.Errorf("could not read Function App %s: %+v", functionApp, err) + } + if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { + if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app slot running in an app service environment.") + } + } + } + return nil + }, + } +} diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index ab19055008ed..0df0d4644294 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -6,6 +6,7 @@ package appservice import ( "context" "fmt" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -70,6 +71,8 @@ type WindowsFunctionAppModel struct { PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` HostingEnvId string `tfschema:"hosting_environment_id"` @@ -104,7 +107,7 @@ func (r WindowsFunctionAppResource) IDValidationFunc() pluginsdk.SchemaValidateF } func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + s := map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, @@ -304,6 +307,15 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { Description: "The local path and filename of the Zip packaged application to deploy to this Windows Function App. **Note:** Using this value requires `WEBSITE_RUN_FROM_PACKAGE=1` to be set on the App in `app_settings`.", }, } + if features.FourPointOhBeta() { + s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + } + } + return s } func (r WindowsFunctionAppResource) Attributes() map[string]*pluginsdk.Schema { @@ -429,6 +441,11 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { availabilityRequest.Name = fmt.Sprintf("%s.%s", functionApp.Name, nameSuffix) availabilityRequest.IsFqdn = pointer.To(true) + if features.FourPointOhBeta() { + if !metadata.ResourceData.Get("vnet_image_pull_enabled").(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } } } } @@ -527,6 +544,10 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { }, } + if features.FourPointOhBeta() { + siteEnvelope.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) + } + pna := helpers.PublicNetworkAccessEnabled if !functionApp.PublicNetworkAccess { pna = helpers.PublicNetworkAccessDisabled @@ -751,6 +772,9 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) + if features.FourPointOhBeta() { + metadata.ResourceData.Set("vnet_image_pull_enabled", pointer.From(props.VnetImagePullEnabled)) + } servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(pointer.From(props.ServerFarmId)) if err != nil { return err @@ -936,6 +960,10 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { model.Properties.HTTPSOnly = pointer.To(state.HttpsOnly) } + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { + model.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) + } + if metadata.ResourceData.HasChange("client_certificate_enabled") { model.Properties.ClientCertEnabled = pointer.To(state.ClientCertEnabled) } @@ -1232,6 +1260,30 @@ func (r WindowsFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { client := metadata.Client.AppService.ServicePlanClient rd := metadata.ResourceDiff + if rd.HasChange("vnet_image_pull_enabled") { + planId := rd.Get("service_plan_id") + // the plan id is known after apply during the initial creation + if planId.(string) == "" { + return nil + } + _, newValue := rd.GetChange("vnet_image_pull_enabled") + servicePlanId, err := commonids.ParseAppServicePlanID(planId.(string)) + if err != nil { + return fmt.Errorf("reading service plan id %+v", err) + } + + asp, err := client.Get(ctx, *servicePlanId) + if err != nil { + return fmt.Errorf("could not read Service Plan %s: %+v", servicePlanId, err) + } + if aspModel := asp.Model; aspModel != nil { + if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && + aspModel.Properties.HostingEnvironmentProfile.Id != nil && *(aspModel.Properties.HostingEnvironmentProfile.Id) != "" && !newValue.(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } + } + if rd.HasChange("service_plan_id") { currentPlanIdRaw, newPlanIdRaw := rd.GetChange("service_plan_id") if newPlanIdRaw.(string) == "" { diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index f311c27c6816..dc9e209bb777 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -6,6 +6,7 @@ package appservice import ( "context" "fmt" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -73,6 +74,7 @@ type WindowsFunctionAppSlotModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider } var _ sdk.ResourceWithUpdate = WindowsFunctionAppSlotResource{} @@ -92,7 +94,7 @@ func (r WindowsFunctionAppSlotResource) IDValidationFunc() pluginsdk.SchemaValid } func (r WindowsFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ + s := map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, @@ -281,6 +283,15 @@ func (r WindowsFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema ValidateFunc: commonids.ValidateSubnetID, }, } + if features.FourPointOhBeta() { + s["vnet_image_pull_enabled"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + Description: "Is container image pull over virtual network enabled? Defaults to `false`.", + } + } + return s } func (r WindowsFunctionAppSlotResource) Attributes() map[string]*pluginsdk.Schema { @@ -444,6 +455,11 @@ func (r WindowsFunctionAppSlotResource) Create() sdk.ResourceFunc { availabilityRequest.Name = fmt.Sprintf("%s.%s", functionAppSlot.Name, nameSuffix) availabilityRequest.IsFqdn = pointer.To(true) + if features.FourPointOhBeta() { + if !metadata.ResourceData.Get("vnet_image_pull_enabled").(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app running in an app service environment.") + } + } } } } @@ -753,6 +769,9 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { state.DefaultHostname = pointer.From(props.DefaultHostName) state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) + if features.FourPointOhBeta() { + metadata.ResourceData.Set("vnet_image_pull_enabled", pointer.From(props.VnetImagePullEnabled)) + } if hostingEnv := props.HostingEnvironmentProfile; hostingEnv != nil { state.HostingEnvId = pointer.From(hostingEnv.Id) } @@ -943,6 +962,10 @@ func (r WindowsFunctionAppSlotResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { + model.Properties.VnetImagePullEnabled = pointer.To(metadata.ResourceData.Get("vnet_image_pull_enabled").(bool)) + } + if metadata.ResourceData.HasChange("storage_account") { storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) if _, err = client.UpdateAzureStorageAccountsSlot(ctx, *id, *storageAccountUpdate); err != nil { @@ -1197,3 +1220,35 @@ func (r WindowsFunctionAppSlotResource) StateUpgraders() sdk.StateUpgradeData { }, } } + +func (r WindowsFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + appClient := metadata.Client.AppService.WebAppsClient + rd := metadata.ResourceDiff + if rd.HasChange("vnet_image_pull_enabled") { + appId := rd.Get("function_app_id") + if appId.(string) == "" { + return nil + } + _, newValue := rd.GetChange("vnet_image_pull_enabled") + functionAppId, err := commonids.ParseAppServiceID(appId.(string)) + if err != nil { + return fmt.Errorf("reading Function App %+v", err) + } + + functionApp, err := appClient.Get(ctx, *functionAppId) + if err != nil { + return fmt.Errorf("could not read Function App %s: %+v", functionApp, err) + } + if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { + if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { + return fmt.Errorf("`vnet_image_pull_enabled` cannot be disabled for app slot running in an app service environment.") + } + } + } + return nil + }, + } +} diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index 198a7a1bff97..c57ea9e5dc16 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -139,7 +139,7 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add in 4.0 provider) +[//]: # (todo: add it in 4.0 provider) [//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) ~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment. diff --git a/website/docs/r/windows_function_app.html.markdown b/website/docs/r/windows_function_app.html.markdown index 91a0da0e20bf..0cada2cda28f 100644 --- a/website/docs/r/windows_function_app.html.markdown +++ b/website/docs/r/windows_function_app.html.markdown @@ -139,6 +139,9 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) +[//]: # (todo: add it in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) + * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. ~> **NOTE:** Setting this value to true will disable the ability to use `zip_deploy_file` which currently relies on the default publishing profile. diff --git a/website/docs/r/windows_function_app_slot.html.markdown b/website/docs/r/windows_function_app_slot.html.markdown index 7041d5d5846c..d95843412299 100644 --- a/website/docs/r/windows_function_app_slot.html.markdown +++ b/website/docs/r/windows_function_app_slot.html.markdown @@ -131,6 +131,9 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) +[//]: # (todo: add it in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) + * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. --- From c94b55d058600bd943825dbb68460f8b22f8242b Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Wed, 3 Apr 2024 13:55:11 +0800 Subject: [PATCH 08/16] fix fmt --- internal/services/appservice/linux_function_app_resource.go | 6 +++--- .../services/appservice/linux_function_app_slot_resource.go | 6 +++--- .../services/appservice/windows_function_app_resource.go | 6 +++--- .../appservice/windows_function_app_slot_resource.go | 6 +++--- website/docs/r/linux_function_app.html.markdown | 4 ++-- website/docs/r/linux_function_app_slot.html.markdown | 4 ++-- website/docs/r/windows_function_app.html.markdown | 6 ++++-- website/docs/r/windows_function_app_slot.html.markdown | 6 ++++-- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index e038ebdd1518..726a79d107b0 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/migration" @@ -1246,12 +1246,12 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { _, newValue := rd.GetChange("vnet_image_pull_enabled") servicePlanId, err := commonids.ParseAppServicePlanID(planId.(string)) if err != nil { - return fmt.Errorf("reading service plan id %+v", err) + return err } asp, err := client.Get(ctx, *servicePlanId) if err != nil { - return fmt.Errorf("could not read Service Plan %s: %+v", servicePlanId, err) + return fmt.Errorf("retrieving %s: %+v", servicePlanId, err) } if aspModel := asp.Model; aspModel != nil { if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 7f8186fd0299..159d1b23ba77 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" @@ -1228,12 +1228,12 @@ func (r LinuxFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { _, newValue := rd.GetChange("vnet_image_pull_enabled") functionAppId, err := commonids.ParseAppServiceID(appId.(string)) if err != nil { - return fmt.Errorf("reading Function App %+v", err) + return err } functionApp, err := appClient.Get(ctx, *functionAppId) if err != nil { - return fmt.Errorf("could not read Function App %s: %+v", functionApp, err) + return fmt.Errorf("retrieving %s: %+v", functionApp, err) } if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index 0df0d4644294..ae372a75e229 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/migration" @@ -1269,12 +1269,12 @@ func (r WindowsFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { _, newValue := rd.GetChange("vnet_image_pull_enabled") servicePlanId, err := commonids.ParseAppServicePlanID(planId.(string)) if err != nil { - return fmt.Errorf("reading service plan id %+v", err) + return err } asp, err := client.Get(ctx, *servicePlanId) if err != nil { - return fmt.Errorf("could not read Service Plan %s: %+v", servicePlanId, err) + return fmt.Errorf("retrieving %s: %+v", servicePlanId, err) } if aspModel := asp.Model; aspModel != nil { if aspModel.Properties != nil && aspModel.Properties.HostingEnvironmentProfile != nil && diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index dc9e209bb777..d0686a0634b1 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" @@ -1235,12 +1235,12 @@ func (r WindowsFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { _, newValue := rd.GetChange("vnet_image_pull_enabled") functionAppId, err := commonids.ParseAppServiceID(appId.(string)) if err != nil { - return fmt.Errorf("reading Function App %+v", err) + return err } functionApp, err := appClient.Get(ctx, *functionAppId) if err != nil { - return fmt.Errorf("could not read Function App %s: %+v", functionApp, err) + return fmt.Errorf("retrieving %s: %+v", functionApp, err) } if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index c57ea9e5dc16..56fd36b1cdd7 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -140,9 +140,9 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) [//]: # (todo: add it in 4.0 provider) -[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) -~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment. +[//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. Must be set to `true` when running in an App Service Environment.) * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index f1f5cb8789e5..aaa55ae991bd 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -133,9 +133,9 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) [//]: # (todo: add in 4.0 provider) -[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) -~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. +[//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. diff --git a/website/docs/r/windows_function_app.html.markdown b/website/docs/r/windows_function_app.html.markdown index 0cada2cda28f..3981525d9cc8 100644 --- a/website/docs/r/windows_function_app.html.markdown +++ b/website/docs/r/windows_function_app.html.markdown @@ -139,8 +139,10 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add it in 4.0 provider) -[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) +[//]: # (todo: add in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) + +[//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. diff --git a/website/docs/r/windows_function_app_slot.html.markdown b/website/docs/r/windows_function_app_slot.html.markdown index d95843412299..f411a21c4bc3 100644 --- a/website/docs/r/windows_function_app_slot.html.markdown +++ b/website/docs/r/windows_function_app_slot.html.markdown @@ -131,8 +131,10 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add it in 4.0 provider) -[//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) +[//]: # (todo: add in 4.0 provider) +[//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) + +[//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) * `webdeploy_publish_basic_authentication_enabled` - (Optional) Should the default WebDeploy Basic Authentication publishing credentials enabled. Defaults to `true`. From 5a6cdf104b6079e294f2f3aad2e9a02463c17759 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Wed, 3 Apr 2024 14:08:54 +0800 Subject: [PATCH 09/16] fix wrong type issue --- .../services/appservice/linux_function_app_slot_resource.go | 2 +- .../services/appservice/windows_function_app_slot_resource.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 159d1b23ba77..5711308af98b 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -1233,7 +1233,7 @@ func (r LinuxFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { functionApp, err := appClient.Get(ctx, *functionAppId) if err != nil { - return fmt.Errorf("retrieving %s: %+v", functionApp, err) + return fmt.Errorf("retrieving %s: %+v", functionAppId, err) } if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index d0686a0634b1..c7124c5762c6 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -1240,7 +1240,7 @@ func (r WindowsFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { functionApp, err := appClient.Get(ctx, *functionAppId) if err != nil { - return fmt.Errorf("retrieving %s: %+v", functionApp, err) + return fmt.Errorf("retrieving %s: %+v", functionAppId, err) } if functionAppModel := functionApp.Model; functionAppModel != nil && functionAppModel.Properties != nil { if ase := functionAppModel.Properties.HostingEnvironmentProfile; ase != nil && ase.Id != nil && *(ase.Id) != "" && !newValue.(bool) { From e83ec2777a598a1eba366a5954531ea12ec32f72 Mon Sep 17 00:00:00 2001 From: Xiaxin Date: Fri, 5 Apr 2024 17:33:18 +0800 Subject: [PATCH 10/16] fix 4.0 comment for linux function app --- .../appservice/linux_function_app_resource.go | 2 +- .../linux_function_app_resource_test.go | 77 ++++++++++++++++++- .../linux_function_app_slot_resource_test.go | 77 +++++++++++++++---- .../docs/r/linux_function_app.html.markdown | 2 +- 4 files changed, 139 insertions(+), 19 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index c05862dfe1f9..93a4495eacf9 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -72,7 +72,7 @@ type LinuxFunctionAppModel struct { PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` - // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // TODO 4.0 not supported on Consumption plans // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index c9be61e07f5a..4d65fb4201dc 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -6,6 +6,7 @@ package appservice_test import ( "context" "fmt" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "regexp" "strings" "testing" @@ -1680,9 +1681,15 @@ func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} + var vnetIntegrationProperties string + if features.FourPointOhBeta() { + vnetIntegrationProperties = r.vNetIntegration_subnet1WithVnetProperties(data, SkuStandardPlan) + } else { + vnetIntegrationProperties = r.vNetIntegration_subnet1(data, SkuStandardPlan) + } data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Config: vnetIntegrationProperties, Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( @@ -3462,8 +3469,6 @@ resource "azurerm_linux_function_app" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true - } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } @@ -4102,6 +4107,7 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_subnet1WithVnetProperties func (r LinuxFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -4156,7 +4162,6 @@ resource "azurerm_linux_function_app" "test" { service_plan_id = azurerm_service_plan.test.id virtual_network_subnet_id = azurerm_subnet.test1.id - // vnet_image_pull_enabled = true storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key @@ -4227,6 +4232,70 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r LinuxFunctionAppResource) vNetIntegration_subnet1WithVnetProperties(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.test1.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + vnet_image_pull_enabled = true + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env func (r LinuxFunctionAppResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %s 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 2227e3dd0c73..79907412ea8e 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -2461,8 +2461,6 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true - tags = { terraform = "true" Env = "AccTest" @@ -2504,9 +2502,6 @@ resource "azurerm_linux_function_app_slot" "test" { virtual_network_subnet_id = azurerm_subnet.test.id } } - - // vnet_image_pull_enabled = true - } `, r.template(data, planSku), data.RandomInteger) } @@ -2645,7 +2640,6 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } @@ -3232,13 +3226,12 @@ resource "azurerm_linux_function_app_slot" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} - - // vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_subnet1WithVnetProperties func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -3294,9 +3287,6 @@ resource "azurerm_linux_function_app_slot" "test" { virtual_network_subnet_id = azurerm_subnet.test1.id site_config {} - - // vnet_image_pull_enabled = true - } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3360,6 +3350,67 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1WithVnetProperties(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 + + vnet_image_pull_enabled = true + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env func (r LinuxFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s @@ -3381,8 +3432,7 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - // vnet_image_pull_enabled = true - + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } @@ -3394,6 +3444,7 @@ resource "azurerm_linux_function_app_slot" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index 198a7a1bff97..c4f11b7191f5 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -139,7 +139,7 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add in 4.0 provider) +[//]: # (TODO 4.0 add it in 4.0 provider) [//]: # (* `vnet_image_pull_enabled` - (Optional) Should the traffic for the image pull be routed over virtual network enabled. Defaults to `false`.) ~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment. From 36b4b3ecc3834444db1b4550dd38a77aecdc1396 Mon Sep 17 00:00:00 2001 From: Xiaxin Date: Fri, 5 Apr 2024 18:28:44 +0800 Subject: [PATCH 11/16] update commit and test case per reviewer's comments --- .../linux_function_app_resource_test.go | 2 +- .../linux_function_app_slot_resource.go | 2 +- .../linux_function_app_slot_resource_test.go | 10 ++- .../windows_function_app_resource.go | 2 +- .../windows_function_app_resource_test.go | 71 ++++++++++++++++++- .../windows_function_app_slot_resource.go | 2 +- ...windows_function_app_slot_resource_test.go | 64 ++++++++++++++++- .../r/linux_function_app_slot.html.markdown | 2 +- .../docs/r/windows_function_app.html.markdown | 2 +- .../r/windows_function_app_slot.html.markdown | 2 +- 10 files changed, 149 insertions(+), 10 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 4d65fb4201dc..ac4b36d320cc 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -6,7 +6,6 @@ package appservice_test import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "regexp" "strings" "testing" @@ -17,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 4ea6ae222ecf..478d645c53ea 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -74,7 +74,7 @@ type LinuxFunctionAppSlotModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` - // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // TODO 4.0 not supported on Consumption plans } var _ sdk.ResourceWithUpdate = LinuxFunctionAppSlotResource{} 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 79907412ea8e..ad17d334b71c 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -1174,9 +1175,16 @@ func TestAccLinuxFunctionAppSlot_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} + var vnetIntegrationProperties string + if features.FourPointOhBeta() { + vnetIntegrationProperties = r.vNetIntegration_subnet1WithVnetProperties(data, SkuStandardPlan) + } else { + vnetIntegrationProperties = r.vNetIntegration_subnet1(data, SkuStandardPlan) + } + data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Config: vnetIntegrationProperties, Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index ae372a75e229..0da038df3d62 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -71,7 +71,7 @@ type WindowsFunctionAppModel struct { PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` - // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // TODO 4.0 not supported on Consumption plans // Computed CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index adf49544cbbc..12036e7e1c92 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -1453,9 +1454,15 @@ func TestAccWindowsFunctionApp_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} + var vnetIntegrationProperties string + if features.FourPointOhBeta() { + vnetIntegrationProperties = r.vNetIntegration_subnet1WithVnetProperties(data, SkuStandardPlan) + } else { + vnetIntegrationProperties = r.vNetIntegration_subnet1(data, SkuStandardPlan) + } data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Config: vnetIntegrationProperties, Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( @@ -3800,6 +3807,67 @@ resource "azurerm_windows_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r WindowsFunctionAppResource) vNetIntegration_subnet1WithVnetProperties(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_windows_function_app" "test" { + name = "acctest-WFA-%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 + vnet_image_pull_enabled = true + + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env func (r WindowsFunctionAppResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -3821,6 +3889,7 @@ resource "azurerm_windows_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index c7124c5762c6..479c25aee2e1 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -74,7 +74,7 @@ type WindowsFunctionAppSlotModel struct { SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` - // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // Not supported on Consumption plans todo add in 4.0 provider + // VnetImagePullEnabled bool `tfschema:"vnet_image_pull_enabled"` // TODO 4.0 not supported on Consumption plans } var _ sdk.ResourceWithUpdate = WindowsFunctionAppSlotResource{} diff --git a/internal/services/appservice/windows_function_app_slot_resource_test.go b/internal/services/appservice/windows_function_app_slot_resource_test.go index 355b39882578..821cac3fd967 100644 --- a/internal/services/appservice/windows_function_app_slot_resource_test.go +++ b/internal/services/appservice/windows_function_app_slot_resource_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -1094,9 +1095,16 @@ func TestAccWindowsFunctionAppSlot_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") r := WindowsFunctionAppSlotResource{} + var vnetIntegrationProperties string + if features.FourPointOhBeta() { + vnetIntegrationProperties = r.vNetIntegration_subnet1WithVnetProperties(data, SkuStandardPlan) + } else { + vnetIntegrationProperties = r.vNetIntegration_subnet1(data, SkuStandardPlan) + } + data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.vNetIntegration_subnet1(data, SkuStandardPlan), + Config: vnetIntegrationProperties, Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("virtual_network_subnet_id").MatchesOtherKey( @@ -3104,6 +3112,58 @@ resource "azurerm_windows_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r WindowsFunctionAppSlotResource) vNetIntegration_subnet1WithVnetProperties(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_windows_function_app_slot" "test" { + name = "acctest-WFAS-%d" + function_app_id = azurerm_windows_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 + vnet_image_pull_enabled = true + + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env func (r WindowsFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s @@ -3125,6 +3185,7 @@ resource "azurerm_windows_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } @@ -3136,6 +3197,7 @@ resource "azurerm_windows_function_app_slot" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index aaa55ae991bd..f03826c75423 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -132,7 +132,7 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add in 4.0 provider) +[//]: # (TODO 4.0 add it in 4.0 provider) [//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) [//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) diff --git a/website/docs/r/windows_function_app.html.markdown b/website/docs/r/windows_function_app.html.markdown index 3981525d9cc8..513e8b538f27 100644 --- a/website/docs/r/windows_function_app.html.markdown +++ b/website/docs/r/windows_function_app.html.markdown @@ -139,7 +139,7 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add in 4.0 provider) +[//]: # (TODO 4.0 add it in 4.0 provider) [//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) [//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) diff --git a/website/docs/r/windows_function_app_slot.html.markdown b/website/docs/r/windows_function_app_slot.html.markdown index f411a21c4bc3..20645cebb6b6 100644 --- a/website/docs/r/windows_function_app_slot.html.markdown +++ b/website/docs/r/windows_function_app_slot.html.markdown @@ -131,7 +131,7 @@ The following arguments are supported: ~> **Note:** Assigning the `virtual_network_subnet_id` property requires [RBAC permissions on the subnet](https://docs.microsoft.com/en-us/azure/app-service/overview-vnet-integration#permissions) -[//]: # (todo: add in 4.0 provider) +[//]: # (TODO 4.0 add it in 4.0 provider) [//]: # (* `vnet_image_pull_enabled` - (Optional) Specifies whether traffic for the image pull should be routed over virtual network. Defaults to `false`.) [//]: # (~> **Note:** The feature can also be enabled via the app setting `WEBSITE_PULL_IMAGE_OVER_VNET`. The Setting is enabled by default for app running in the App Service Environment.) From 552e76907ad2dc5cfeeda9659453cb833f80b296 Mon Sep 17 00:00:00 2001 From: Xiaxin Date: Fri, 5 Apr 2024 18:32:36 +0800 Subject: [PATCH 12/16] sorting imports --- internal/services/appservice/linux_function_app_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 9f795f3d23cf..6994d77232dd 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/migration" From 99d5023dc8b2666dce1f232a6c2df63565181abd Mon Sep 17 00:00:00 2001 From: Xiaxin Date: Fri, 5 Apr 2024 18:42:33 +0800 Subject: [PATCH 13/16] fmt --- .../services/appservice/linux_function_app_slot_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 478d645c53ea..a97b25c94451 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -6,7 +6,6 @@ package appservice import ( "context" "fmt" - "github.com/hashicorp/terraform-provider-azurerm/internal/features" "strconv" "strings" "time" @@ -20,6 +19,7 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/resourceproviders" "github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/webapps" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers" From 1c36adb0dd485a0d99d266f4ac4f991ccae43ad4 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Thu, 30 May 2024 17:05:16 +0800 Subject: [PATCH 14/16] add new test cases for the vnet_image_pull_enable property with 4.0 flag enabled --- .../appservice/linux_function_app_resource.go | 2 +- .../linux_function_app_resource_test.go | 297 ++++++- .../linux_function_app_slot_resource.go | 2 +- .../linux_function_app_slot_resource_test.go | 835 ++++++++++++++++-- 4 files changed, 1059 insertions(+), 77 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index aefbf81dd610..0cb0cd009703 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -1238,7 +1238,7 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AppService.ServicePlanClient rd := metadata.ResourceDiff - if rd.HasChange("vnet_image_pull_enabled") { + if rd.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { planId := rd.Get("service_plan_id") // the plan id is known after apply during the initial creation if planId.(string) == "" { diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index b0c7ef8b8828..75e0beecf071 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -604,6 +605,24 @@ func TestAccLinuxFunctionApp_consumptionCompleteUpdate(t *testing.T) { }) } +func TestAccLinuxFunctionApp_elasticPremiumCompleteWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.elasticCompleteWithVnetProperties(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("site_config.0.elastic_instance_minimum").HasValue("5"), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} func TestAccLinuxFunctionApp_elasticPremiumComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1564,6 +1583,24 @@ func TestAccLinuxFunctionAppASEv3_basic(t *testing.T) { }) } +func TestAccLinuxFunctionAppASEv3_basicWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3VnetProperties(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionApp_corsUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1693,6 +1730,27 @@ func TestAccLinuxFunctionApp_basicPlanBackupShouldError(t *testing.T) { }) } +func TestAccLinuxFunctionApp_vNetIntegrationWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_subnetWithVnetProperties(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.test1").Key("id"), + ), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -3344,7 +3402,7 @@ resource "azurerm_linux_function_app" "test" { `, r.storageContainerTemplate(data, planSku), data.RandomInteger, data.Client().TenantID) } -func (r LinuxFunctionAppResource) elasticComplete(data acceptance.TestData) string { +func (r LinuxFunctionAppResource) elasticCompleteWithVnetProperties(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3482,12 +3540,154 @@ resource "azurerm_linux_function_app" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true + vnet_image_pull_enabled = true } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } +func (r LinuxFunctionAppResource) elasticComplete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + app_settings = { + foo = "bar" + secret = "sauce" + } + + backup { + name = "acctest" + storage_account_url = "https://${azurerm_storage_account.test.name}.blob.core.windows.net/${azurerm_storage_container.test.name}${data.azurerm_storage_account_sas.test.sas}&sr=b" + schedule { + frequency_interval = 7 + frequency_unit = "Day" + } + } + + connection_string { + name = "Example" + value = "some-postgresql-connection-string" + type = "PostgreSQL" + } + + site_config { + app_command_line = "whoami" + api_definition_url = "https://example.com/azure_function_app_def.json" + // api_management_api_id = "" // TODO + application_insights_connection_string = azurerm_application_insights.test.connection_string + + application_stack { + python_version = "3.8" + } + + elastic_instance_minimum = 5 + + container_registry_use_managed_identity = true + container_registry_managed_identity_client_id = azurerm_user_assigned_identity.test.client_id + + default_documents = [ + "first.html", + "second.jsp", + "third.aspx", + "hostingstart.html", + ] + + http2_enabled = true + + ip_restriction { + ip_address = "10.10.10.10/32" + name = "test-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + load_balancing_mode = "LeastResponseTime" + pre_warmed_instance_count = 2 + remote_debugging_enabled = true + remote_debugging_version = "VS2017" + + scm_ip_restriction { + ip_address = "10.20.20.20/32" + name = "test-scm-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + scm_ip_restriction { + ip_address = "fd80::/64" + name = "test-scm-restriction-v6" + priority = 124 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + use_32_bit_worker = true + websockets_enabled = true + ftps_state = "FtpsOnly" + health_check_path = "/health-check" + worker_count = 3 + + minimum_tls_version = "1.1" + scm_minimum_tls_version = "1.1" + + cors { + allowed_origins = [ + "https://www.contoso.com", + "www.contoso.com", + ] + + support_credentials = true + } + + vnet_route_all_enabled = true + } +} +`, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) +} + func (r LinuxFunctionAppResource) servicePlanUpdate(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -4122,6 +4322,69 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r LinuxFunctionAppResource) vNetIntegration_subnetWithVnetProperties(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.test1.id + + vnet_image_pull_enabled = true + 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_subnet1(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -4176,7 +4439,6 @@ resource "azurerm_linux_function_app" "test" { service_plan_id = azurerm_service_plan.test.id virtual_network_subnet_id = azurerm_subnet.test1.id - // vnet_image_pull_enabled = true storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key @@ -4267,7 +4529,34 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - // vnet_image_pull_enabled = true + site_config { + vnet_route_all_enabled = true + } +} +`, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) +} + +func (r LinuxFunctionAppResource) withASEV3VnetProperties(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_storage_account" "test" { + name = "acctestsa%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +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 + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index 9e0c81b31e9e..41367dbadddb 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -1221,7 +1221,7 @@ func (r LinuxFunctionAppSlotResource) CustomizeDiff() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { appClient := metadata.Client.AppService.WebAppsClient rd := metadata.ResourceDiff - if rd.HasChange("vnet_image_pull_enabled") { + if rd.HasChange("vnet_image_pull_enabled") && features.FourPointOhBeta() { appId := rd.Get("function_app_id") if appId.(string) == "" { return nil 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 2227e3dd0c73..757384bdb42e 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) @@ -337,6 +338,24 @@ func TestAccLinuxFunctionAppSlot_consumptionCompleteUpdate(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlot_elasticPremiumCompleteWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.elasticCompleteWithVnetProperties(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionAppSlot_elasticPremiumComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -352,6 +371,24 @@ func TestAccLinuxFunctionAppSlot_elasticPremiumComplete(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlot_standardCompleteWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.standardCompleteWithVnetProperties(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionAppSlot_standardComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -385,6 +422,24 @@ func TestAccLinuxFunctionAppSlot_withAuthSettingsStandard(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlot_scmIpRestrictionSubnetWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.scmIpRestrictionSubnetWithVnetProperties(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionAppSlot_scmIpRestrictionSubnet(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -1230,6 +1285,51 @@ func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdate(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdateWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basicWithVnetProperties(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_subnet1WithVnetProperties(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.test1").Key("id"), + ), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_subnet2WithVnetProperties(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"), + ), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_basicWithVnetProperties(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionAppSlotASEv3_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -1245,6 +1345,24 @@ func TestAccLinuxFunctionAppSlotASEv3_basic(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlotASEv3_basicWithVnetProperties(t *testing.T) { + if !features.FourPointOhBeta() { + t.Skip("this test requires 4.0 mode") + } + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withASEV3WithVnetProperties(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + func TestAccLinuxFunctionAppSlot_withStorageAccountBlock(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -2282,7 +2400,7 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.Client().TenantID) } -func (r LinuxFunctionAppSlotResource) standardComplete(data acceptance.TestData) string { +func (r LinuxFunctionAppSlotResource) standardCompleteWithVnetProperties(data acceptance.TestData) string { planSku := "S1" return fmt.Sprintf(` provider "azurerm" { @@ -2461,7 +2579,7 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true + vnet_image_pull_enabled = true tags = { terraform = "true" @@ -2471,47 +2589,8 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.storageContainerTemplate(data, planSku), data.RandomInteger, data.Client().TenantID) } -func (r LinuxFunctionAppSlotResource) scmIpRestrictionSubnet(data acceptance.TestData, planSku string) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -%s - -resource "azurerm_virtual_network" "test" { - name = "acctestvirtnet%[2]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" "test" { - name = "acctestsubnet%[2]d" - resource_group_name = azurerm_resource_group.test.name - virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.0.2.0/24"] -} - -resource "azurerm_linux_function_app_slot" "test" { - name = "acctest-LFAS-%[2]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 - - site_config { - scm_ip_restriction { - virtual_network_subnet_id = azurerm_subnet.test.id - } - } - - // vnet_image_pull_enabled = true - -} -`, r.template(data, planSku), data.RandomInteger) -} - -func (r LinuxFunctionAppSlotResource) elasticComplete(data acceptance.TestData) string { +func (r LinuxFunctionAppSlotResource) standardComplete(data acceptance.TestData) string { + planSku := "S1" return fmt.Sprintf(` provider "azurerm" { features {} @@ -2543,6 +2622,33 @@ resource "azurerm_linux_function_app_slot" "test" { secret = "sauce" } + auth_settings { + enabled = true + issuer = "https://sts.windows.net/%[3]s" + + additional_login_parameters = { + test_key = "test_value" + } + + active_directory { + client_id = "aadclientid" + client_secret = "aadsecret" + + allowed_audiences = [ + "activedirectorytokenaudiences", + ] + } + + facebook { + app_id = "facebookappid" + app_secret = "facebookappsecret" + + oauth_scopes = [ + "facebookscope", + ] + } + } + backup { name = "acctest" storage_account_url = "https://${azurerm_storage_account.test.name}.blob.core.windows.net/${azurerm_storage_container.test.name}${data.azurerm_storage_account_sas.test.sas}&sr=b" @@ -2552,13 +2658,28 @@ resource "azurerm_linux_function_app_slot" "test" { } } + builtin_logging_enabled = false + client_certificate_enabled = true + client_certificate_mode = "OptionalInteractiveUser" + client_certificate_exclusion_paths = "/foo;/bar;/hello;/world" + connection_string { - name = "Example" + name = "First" value = "some-postgresql-connection-string" type = "PostgreSQL" } + enabled = false + functions_extension_version = "~3" + https_only = true + + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id] + } + site_config { + always_on = true app_command_line = "whoami" api_definition_url = "https://example.com/azure_function_app_def.json" // api_management_api_id = "" // TODO @@ -2592,6 +2713,7 @@ resource "azurerm_linux_function_app_slot" "test" { x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] x_forwarded_host = ["example.com"] } + description = "Allow ip address 10.10.10.10/32" } load_balancing_mode = "LeastResponseTime" @@ -2645,12 +2767,16 @@ resource "azurerm_linux_function_app_slot" "test" { vnet_route_all_enabled = true } - // vnet_image_pull_enabled = true + + tags = { + terraform = "true" + Env = "AccTest" + } } -`, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) +`, r.storageContainerTemplate(data, planSku), data.RandomInteger, data.Client().TenantID) } -func (r LinuxFunctionAppSlotResource) updateStorageAccount(data acceptance.TestData, planSku string) string { +func (r LinuxFunctionAppSlotResource) scmIpRestrictionSubnetWithVnetProperties(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2658,18 +2784,37 @@ provider "azurerm" { %s +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%[2]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" "test" { + name = "acctestsubnet%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] +} + resource "azurerm_linux_function_app_slot" "test" { - name = "acctest-LFAS-%d" + name = "acctest-LFAS-%[2]d" function_app_id = azurerm_linux_function_app.test.id - storage_account_name = azurerm_storage_account.update.name - storage_account_access_key = azurerm_storage_account.update.primary_access_key + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key - site_config {} + site_config { + scm_ip_restriction { + virtual_network_subnet_id = azurerm_subnet.test.id + } + } + + vnet_image_pull_enabled = true } -`, r.templateExtraStorageAccount(data, planSku), data.RandomInteger) +`, r.template(data, planSku), data.RandomInteger) } - -func (r LinuxFunctionAppSlotResource) identitySystemAssigned(data acceptance.TestData, planSku string) string { +func (r LinuxFunctionAppSlotResource) scmIpRestrictionSubnet(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2677,22 +2822,36 @@ provider "azurerm" { %s +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%[2]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" "test" { + name = "acctestsubnet%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.2.0/24"] +} + resource "azurerm_linux_function_app_slot" "test" { - name = "acctest-LFAS-%d" + name = "acctest-LFAS-%[2]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 - site_config {} - - identity { - type = "SystemAssigned" + site_config { + scm_ip_restriction { + virtual_network_subnet_id = azurerm_subnet.test.id + } } } -`, r.identityTemplate(data, planSku), data.RandomInteger) +`, r.template(data, planSku), data.RandomInteger) } -func (r LinuxFunctionAppSlotResource) identitySystemAssignedUserAssigned(data acceptance.TestData, planSku string) string { +func (r LinuxFunctionAppSlotResource) elasticCompleteWithVnetProperties(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2700,13 +2859,331 @@ provider "azurerm" { %s -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 - - site_config {} +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%[2]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 + + app_settings = { + foo = "bar" + secret = "sauce" + } + + backup { + name = "acctest" + storage_account_url = "https://${azurerm_storage_account.test.name}.blob.core.windows.net/${azurerm_storage_container.test.name}${data.azurerm_storage_account_sas.test.sas}&sr=b" + schedule { + frequency_interval = 7 + frequency_unit = "Day" + } + } + + connection_string { + name = "Example" + value = "some-postgresql-connection-string" + type = "PostgreSQL" + } + + site_config { + app_command_line = "whoami" + api_definition_url = "https://example.com/azure_function_app_def.json" + // api_management_api_id = "" // TODO + application_insights_key = azurerm_application_insights.test.instrumentation_key + application_insights_connection_string = azurerm_application_insights.test.connection_string + + application_stack { + python_version = "3.8" + } + + container_registry_use_managed_identity = true + container_registry_managed_identity_client_id = azurerm_user_assigned_identity.test.client_id + + default_documents = [ + "first.html", + "second.jsp", + "third.aspx", + "hostingstart.html", + ] + + http2_enabled = true + + ip_restriction { + ip_address = "10.10.10.10/32" + name = "test-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + load_balancing_mode = "LeastResponseTime" + pre_warmed_instance_count = 2 + remote_debugging_enabled = true + remote_debugging_version = "VS2017" + + scm_ip_restriction { + ip_address = "10.20.20.20/32" + name = "test-scm-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + scm_ip_restriction { + ip_address = "fd80::/64" + name = "test-scm-restriction-v6" + priority = 124 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + use_32_bit_worker = true + websockets_enabled = true + ftps_state = "FtpsOnly" + health_check_path = "/health-check" + worker_count = 3 + + minimum_tls_version = "1.1" + scm_minimum_tls_version = "1.1" + + cors { + allowed_origins = [ + "https://www.contoso.com", + "www.contoso.com", + ] + + support_credentials = true + } + + vnet_route_all_enabled = true + } + vnet_image_pull_enabled = true +} +`, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) +} +func (r LinuxFunctionAppSlotResource) elasticComplete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_application_insights" "test" { + name = "acctestappinsights-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%[2]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 + + app_settings = { + foo = "bar" + secret = "sauce" + } + + backup { + name = "acctest" + storage_account_url = "https://${azurerm_storage_account.test.name}.blob.core.windows.net/${azurerm_storage_container.test.name}${data.azurerm_storage_account_sas.test.sas}&sr=b" + schedule { + frequency_interval = 7 + frequency_unit = "Day" + } + } + + connection_string { + name = "Example" + value = "some-postgresql-connection-string" + type = "PostgreSQL" + } + + site_config { + app_command_line = "whoami" + api_definition_url = "https://example.com/azure_function_app_def.json" + // api_management_api_id = "" // TODO + application_insights_key = azurerm_application_insights.test.instrumentation_key + application_insights_connection_string = azurerm_application_insights.test.connection_string + + application_stack { + python_version = "3.8" + } + + container_registry_use_managed_identity = true + container_registry_managed_identity_client_id = azurerm_user_assigned_identity.test.client_id + + default_documents = [ + "first.html", + "second.jsp", + "third.aspx", + "hostingstart.html", + ] + + http2_enabled = true + + ip_restriction { + ip_address = "10.10.10.10/32" + name = "test-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + load_balancing_mode = "LeastResponseTime" + pre_warmed_instance_count = 2 + remote_debugging_enabled = true + remote_debugging_version = "VS2017" + + scm_ip_restriction { + ip_address = "10.20.20.20/32" + name = "test-scm-restriction" + priority = 123 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + scm_ip_restriction { + ip_address = "fd80::/64" + name = "test-scm-restriction-v6" + priority = 124 + action = "Allow" + headers { + x_azure_fdid = ["55ce4ed1-4b06-4bf1-b40e-4638452104da"] + x_fd_health_probe = ["1"] + x_forwarded_for = ["9.9.9.9/32", "2002::1234:abcd:ffff:c0a8:101/64"] + x_forwarded_host = ["example.com"] + } + } + + use_32_bit_worker = true + websockets_enabled = true + ftps_state = "FtpsOnly" + health_check_path = "/health-check" + worker_count = 3 + + minimum_tls_version = "1.1" + scm_minimum_tls_version = "1.1" + + cors { + allowed_origins = [ + "https://www.contoso.com", + "www.contoso.com", + ] + + support_credentials = true + } + + vnet_route_all_enabled = true + } +} +`, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) updateStorageAccount(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +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.update.name + storage_account_access_key = azurerm_storage_account.update.primary_access_key + + site_config {} +} +`, r.templateExtraStorageAccount(data, planSku), data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) identitySystemAssigned(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +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 + + site_config {} + + identity { + type = "SystemAssigned" + } +} +`, r.identityTemplate(data, planSku), data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) identitySystemAssignedUserAssigned(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +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 + + site_config {} identity { type = "SystemAssigned, UserAssigned" @@ -3225,6 +3702,65 @@ resource "azurerm_subnet" "test2" { } } +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 + + site_config {} +} + +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) vNetIntegration_basicWithVnetProperties(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 @@ -3233,7 +3769,7 @@ resource "azurerm_linux_function_app_slot" "test" { site_config {} - // vnet_image_pull_enabled = true + vnet_image_pull_enabled = true } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) @@ -3294,9 +3830,66 @@ resource "azurerm_linux_function_app_slot" "test" { virtual_network_subnet_id = azurerm_subnet.test1.id site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1WithVnetProperties(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"] + } + } +} - // vnet_image_pull_enabled = true +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 + vnet_image_pull_enabled = true + site_config {} } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3360,6 +3953,66 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet2WithVnetProperties(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.test2.id + + vnet_image_pull_enabled = true + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + func (r LinuxFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s @@ -3381,7 +4034,46 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - // vnet_image_pull_enabled = true + site_config { + vnet_route_all_enabled = true + } +} + +resource "azurerm_linux_function_app_slot" "test" { + name = "acctest-LFAS-%[3]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 + + site_config { + vnet_route_all_enabled = true + } +} + +`, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) +} +func (r LinuxFunctionAppSlotResource) withASEV3WithVnetProperties(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_linux_function_app" "test" { + name = "acctest-LFA-%[3]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true @@ -3394,6 +4086,7 @@ resource "azurerm_linux_function_app_slot" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } From 432dfe56a8cb12aad7e3ebb3224a4a24dbdb788b Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Fri, 31 May 2024 13:09:05 +0800 Subject: [PATCH 15/16] add 4.0 flag for test cases based on the reviewer's comments --- .../linux_function_app_resource_test.go | 114 +++++++++++++++++- .../linux_function_app_slot_resource_test.go | 77 +++--------- 2 files changed, 127 insertions(+), 64 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 046cc11c98fc..017c7a0f077c 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -623,6 +623,8 @@ func TestAccLinuxFunctionApp_elasticPremiumCompleteWithVnetProperties(t *testing data.ImportStep("site_credential.0.password"), }) } + +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionApp_elasticPremiumComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1568,6 +1570,7 @@ func TestAccLinuxFunctionApp_storageAccountKeyVaultSecretVersionless(t *testing. }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppASEv3_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1751,6 +1754,7 @@ func TestAccLinuxFunctionApp_vNetIntegrationWithVnetProperties(t *testing.T) { }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1775,6 +1779,7 @@ func TestAccLinuxFunctionApp_vNetIntegration(t *testing.T) { }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionApp_vNetIntegrationUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -1817,6 +1822,48 @@ func TestAccLinuxFunctionApp_vNetIntegrationUpdate(t *testing.T) { }) } +func TestAccLinuxFunctionApp_vNetIntegrationUpdateWithVnetProperties(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_subnet1WithVnetProperties(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.test1").Key("id"), + ), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_subnet2WithVnetProperties(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"), + ), + ), + }, + data.ImportStep("site_credential.0.password"), + { + Config: r.vNetIntegration_basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("site_credential.0.password"), + }) +} + // Outputs func TestAccLinuxFunctionApp_basicOutputs(t *testing.T) { @@ -3552,6 +3599,7 @@ resource "azurerm_linux_function_app" "test" { `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_subnet1WithVnetProperties func (r LinuxFunctionAppResource) elasticComplete(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -4454,6 +4502,7 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_subnet2WithVnetProperties func (r LinuxFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -4579,7 +4628,70 @@ resource "azurerm_linux_function_app" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } -// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env +func (r LinuxFunctionAppResource) vNetIntegration_subnet2WithVnetProperties(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.test2.id + + storage_account_name = azurerm_storage_account.test.name + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + vnet_image_pull_enabled = true + site_config {} +} +`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) +} + +// TODO 4.0 remove this test case as it's replaced by withASEV3VnetProperties func (r LinuxFunctionAppResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %s 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 5c0f297a55cf..3644e920c10b 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -356,6 +356,7 @@ func TestAccLinuxFunctionAppSlot_elasticPremiumCompleteWithVnetProperties(t *tes }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppSlot_elasticPremiumComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -389,6 +390,7 @@ func TestAccLinuxFunctionAppSlot_standardCompleteWithVnetProperties(t *testing.T }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppSlot_standardComplete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -440,6 +442,7 @@ func TestAccLinuxFunctionAppSlot_scmIpRestrictionSubnetWithVnetProperties(t *tes }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppSlot_scmIpRestrictionSubnet(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -1250,6 +1253,7 @@ func TestAccLinuxFunctionAppSlot_vNetIntegration(t *testing.T) { }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -1337,6 +1341,7 @@ func TestAccLinuxFunctionAppSlot_vNetIntegrationUpdateWithVnetProperties(t *test }) } +// TODO 4.0 remove post 4.0 func TestAccLinuxFunctionAppSlotASEv3_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") r := LinuxFunctionAppSlotResource{} @@ -2596,6 +2601,7 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.storageContainerTemplate(data, planSku), data.RandomInteger, data.Client().TenantID) } +// TODO 4.0 remove this test case as it's replaced by standardCompleteWithVnetProperties func (r LinuxFunctionAppSlotResource) standardComplete(data acceptance.TestData) string { planSku := "S1" return fmt.Sprintf(` @@ -2821,6 +2827,8 @@ resource "azurerm_linux_function_app_slot" "test" { } `, r.template(data, planSku), data.RandomInteger) } + +// TODO 4.0 remove this test case as it's replaced by scmIpRestrictionSubnetWithVnetProperties func (r LinuxFunctionAppSlotResource) scmIpRestrictionSubnet(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -2996,6 +3004,8 @@ resource "azurerm_linux_function_app_slot" "test" { } `, r.storageContainerTemplate(data, SkuElasticPremiumPlan), data.RandomInteger) } + +// TODO 4.0 remove this test case as it's replaced by elasticCompleteWithVnetProperties func (r LinuxFunctionAppSlotResource) elasticComplete(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -3662,6 +3672,7 @@ resource "azurerm_user_assigned_identity" "test" { `, r.template(data, planSku), data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_basicWithVnetProperties func (r LinuxFunctionAppSlotResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -3902,6 +3913,7 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by vNetIntegration_subnet2WithVnetProperties func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet2(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -3961,67 +3973,6 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } -func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet1WithVnetProperties(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 - - vnet_image_pull_enabled = true - site_config {} -} -`, r.template(data, planSku), data.RandomInteger, data.RandomInteger) -} - -// TODO 4.0 enable the vnet_image_pull_enabled property for app running in ase env func (r LinuxFunctionAppSlotResource) vNetIntegration_subnet2WithVnetProperties(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" { @@ -4082,6 +4033,7 @@ resource "azurerm_linux_function_app_slot" "test" { `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } +// TODO 4.0 remove this test case as it's replaced by withASEV3WithVnetProperties func (r LinuxFunctionAppSlotResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s @@ -4103,7 +4055,6 @@ resource "azurerm_linux_function_app" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } @@ -4115,7 +4066,6 @@ resource "azurerm_linux_function_app_slot" "test" { storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key - // vnet_image_pull_enabled = true site_config { vnet_route_all_enabled = true } @@ -4123,6 +4073,7 @@ resource "azurerm_linux_function_app_slot" "test" { `, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) } + func (r LinuxFunctionAppSlotResource) withASEV3WithVnetProperties(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s From a1fa3391dd19a7dfe1541865dafa85aa17a3bfb7 Mon Sep 17 00:00:00 2001 From: "Xiaxin Yi (from Dev Box)" Date: Fri, 31 May 2024 15:53:05 +0800 Subject: [PATCH 16/16] add unparam linter --- internal/services/appservice/linux_function_app_resource_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 017c7a0f077c..7a7f1a253926 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -4312,6 +4312,7 @@ resource "azurerm_user_assigned_identity" "test" { `, r.template(data, planSku), data.RandomInteger) } +// nolint:unparam func (r LinuxFunctionAppResource) vNetIntegration_basic(data acceptance.TestData, planSku string) string { return fmt.Sprintf(` provider "azurerm" {