From ff144da04ffdf3d64ff51d8a600bee9bb1bdb0d9 Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Fri, 6 May 2022 14:24:04 +0200 Subject: [PATCH 01/11] add storage account config to function apps --- .../appservice/linux_function_app_resource.go | 28 ++- .../linux_function_app_resource_test.go | 200 +++++++++++++++-- .../windows_function_app_resource.go | 27 +++ .../windows_function_app_resource_test.go | 206 +++++++++++++++++- 4 files changed, 433 insertions(+), 28 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 84b0a290b071..3880aa8c9ec8 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -55,6 +55,7 @@ type LinuxFunctionAppModel struct { HttpsOnly bool `tfschema:"https_only"` KeyVaultReferenceIdentityID string `tfschema:"key_vault_reference_identity_id"` SiteConfig []helpers.SiteConfigLinuxFunctionApp `tfschema:"site_config"` + StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` @@ -251,6 +252,8 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), + "storage_account": helpers.StorageAccountSchema(), + "tags": tags.Schema(), "virtual_network_subnet_id": { @@ -511,6 +514,15 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { } } + storageConfig := helpers.ExpandStorageConfig(functionApp.StorageAccounts) + if storageConfig.Properties != nil { + if _, err := client.UpdateAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName, *storageConfig); err != nil { + if err != nil { + return fmt.Errorf("setting Storage Accounts for Linux %s: %+v", id, err) + } + } + } + connectionStrings := helpers.ExpandConnectionStrings(functionApp.ConnectionStrings) if connectionStrings.Properties != nil { if _, err := client.UpdateConnectionStrings(ctx, id.ResourceGroup, id.SiteName, *connectionStrings); err != nil { @@ -568,6 +580,11 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { return fmt.Errorf("reading Sticky Settings for Linux %s: %+v", id, err) } + storageAccounts, err := client.ListAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName) + if err != nil { + return fmt.Errorf("reading Storage Account information for Linux %s: %+v", id, err) + } + siteCredentialsFuture, err := client.ListPublishingCredentials(ctx, id.ResourceGroup, id.SiteName) if err != nil { return fmt.Errorf("listing Site Publishing Credential information for Linux %s: %+v", id, err) @@ -648,6 +665,8 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { state.SiteConfig[0].AppServiceLogs = helpers.FlattenFunctionAppAppServiceLogs(logs) + state.StorageAccounts = helpers.FlattenStorageAccounts(storageAccounts) + state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) @@ -777,6 +796,13 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { existing.Tags = tags.FromTypedObject(state.Tags) } + if metadata.ResourceData.HasChange("storage_account") { + storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) + if _, err := client.UpdateAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName, *storageAccountUpdate); err != nil { + return fmt.Errorf("updating Storage Accounts for Linux %s: %+v", id, err) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { @@ -789,7 +815,7 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { if sendContentSettings { appSettingsResp, err := client.ListApplicationSettings(ctx, id.ResourceGroup, id.SiteName) if err != nil { - return fmt.Errorf("reading App Settings for Windows %s: %+v", id, err) + return fmt.Errorf("reading App Settings for Linux %s: %+v", id, err) } if state.AppSettings == nil { state.AppSettings = make(map[string]string) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 35f0ac14565d..2efe6562ac4c 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -535,6 +535,57 @@ func TestAccLinuxFunctionApp_withAuthSettingsStandard(t *testing.T) { }) } +func TestAccLinuxFunctionApp_withStorageAccount(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccount(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionApp_withStorageAccountUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccount(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccLinuxFunctionApp_scmIpRestrictionSubnet(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} @@ -3397,25 +3448,20 @@ func (r LinuxFunctionAppResource) vNetIntegration_basic(data acceptance.TestData 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"] @@ -3449,10 +3495,7 @@ resource "azurerm_linux_function_app" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} - } - - `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3461,9 +3504,7 @@ func (r LinuxFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestDa provider "azurerm" { features {} } - %s - resource "azurerm_virtual_network" "test" { name = "vnet-%d" address_space = ["10.0.0.0/16"] @@ -3479,7 +3520,6 @@ resource "azurerm_subnet" "test1" { delegation { name = "delegation" - service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3495,26 +3535,22 @@ resource "azurerm_subnet" "test2" { delegation { name = "delegation" - service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] } } } - resource "azurerm_linux_function_app" "test" { name = "acctest-LFA-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name service_plan_id = azurerm_service_plan.test.id virtual_network_subnet_id = azurerm_subnet.test1.id - storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} - } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3524,25 +3560,20 @@ func (r LinuxFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestDa 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"] @@ -3558,7 +3589,6 @@ resource "azurerm_subnet" "test2" { delegation { name = "delegation" - service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3577,7 +3607,6 @@ resource "azurerm_linux_function_app" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} - } `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3585,7 +3614,6 @@ resource "azurerm_linux_function_app" "test" { func (r LinuxFunctionAppResource) withASEV3(data acceptance.TestData) string { return fmt.Sprintf(` %s - resource "azurerm_storage_account" "test" { name = "acctestsa%s" resource_group_name = azurerm_resource_group.test.name @@ -3607,6 +3635,130 @@ resource "azurerm_linux_function_app" "test" { vnet_route_all_enabled = true } } - `, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) } + +func (r LinuxFunctionAppResource) withStorageAccount(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_linux_function_app" "test" { + name = "acctestWA-%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 + site_config {} + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/files" + } +} +`, r.templateWithStorageAccount(data), data.RandomInteger) +} + +func (r LinuxFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} +%s +resource "azurerm_linux_function_app" "test" { + name = "acctestWA-%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 + site_config {} + storage_account { + name = "updatedfiles" + type = "AzureBlob" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/blob" + } +} +`, r.templateWithStorageAccount(data), data.RandomInteger) +} + +func (r LinuxFunctionAppResource) templateWithStorageAccount(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} +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_storage_container" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} +resource "azurerm_storage_share" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + resource_types { + service = false + container = false + object = true + } + services { + blob = true + queue = false + table = false + file = false + } + start = "2021-04-01" + expiry = "2024-03-30" + permissions { + read = false + write = true + delete = false + list = false + add = false + create = false + update = false + process = false + tag = false + filter = false + } +} +`, r.standardPlanTemplate(data), data.RandomInteger, data.RandomString) +} + +func (LinuxFunctionAppResource) standardPlanTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} +resource "azurerm_service_plan" "test" { + name = "acctestASP-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + os_type = "Linux" + sku_name = "S1" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index 6947a12238cb..01a9ebd09224 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -55,6 +55,7 @@ type WindowsFunctionAppModel struct { HttpsOnly bool `tfschema:"https_only"` KeyVaultReferenceIdentityID string `tfschema:"key_vault_reference_identity_id"` SiteConfig []helpers.SiteConfigWindowsFunctionApp `tfschema:"site_config"` + StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` Tags map[string]string `tfschema:"tags"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` @@ -251,6 +252,8 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), + "storage_account": helpers.StorageAccountSchema(), + "tags": tags.Schema(), "virtual_network_subnet_id": { @@ -397,6 +400,7 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { storageString = fmt.Sprintf(helpers.StorageStringFmt, functionApp.StorageAccountName, functionApp.StorageAccountKey, metadata.Client.Account.Environment.StorageEndpointSuffix) } } + siteConfig, err := helpers.ExpandSiteConfigWindowsFunctionApp(functionApp.SiteConfig, nil, metadata, functionApp.FunctionExtensionsVersion, storageString, functionApp.StorageUsesMSI) if err != nil { return fmt.Errorf("expanding site_config for Windows %s: %+v", id, err) @@ -510,6 +514,15 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { } } + storageConfig := helpers.ExpandStorageConfig(functionApp.StorageAccounts) + if storageConfig.Properties != nil { + if _, err := client.UpdateAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName, *storageConfig); err != nil { + if err != nil { + return fmt.Errorf("setting Storage Accounts for Windows %s: %+v", id, err) + } + } + } + connectionStrings := helpers.ExpandConnectionStrings(functionApp.ConnectionStrings) if connectionStrings.Properties != nil { if _, err := client.UpdateConnectionStrings(ctx, id.ResourceGroup, id.SiteName, *connectionStrings); err != nil { @@ -567,6 +580,11 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { return fmt.Errorf("reading Sticky Settings for Linux %s: %+v", id, err) } + storageAccounts, err := client.ListAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName) + if err != nil { + return fmt.Errorf("reading Storage Account information for Linux %s: %+v", id, err) + } + siteCredentialsFuture, err := client.ListPublishingCredentials(ctx, id.ResourceGroup, id.SiteName) if err != nil { return fmt.Errorf("listing Site Publishing Credential information for Windows %s: %+v", id, err) @@ -647,6 +665,8 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { state.SiteConfig[0].AppServiceLogs = helpers.FlattenFunctionAppAppServiceLogs(logs) + state.StorageAccounts = helpers.FlattenStorageAccounts(storageAccounts) + state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) @@ -776,6 +796,13 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("storage_account") { + storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) + if _, err := client.UpdateAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName, *storageAccountUpdate); err != nil { + return fmt.Errorf("updating Storage Accounts for Linux %s: %+v", id, err) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index f195f04dc627..a59278c60d5e 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -527,6 +527,57 @@ func TestAccWindowsFunctionApp_withAuthSettingsStandard(t *testing.T) { }) } +func TestAccWindowsFunctionApp_withStorageAccount(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccount(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionApp_withStorageAccountUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccount(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccWindowsFunctionApp_builtInLogging(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} @@ -3156,8 +3207,10 @@ resource "azurerm_subnet" "test1" { resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] + delegation { name = "delegation" + service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3170,8 +3223,10 @@ resource "azurerm_subnet" "test2" { 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"] @@ -3187,9 +3242,9 @@ resource "azurerm_windows_function_app" "test" { virtual_network_subnet_id = azurerm_subnet.test1.id storage_account_name = azurerm_storage_account.test.name storage_account_access_key = azurerm_storage_account.test.primary_access_key + site_config {} } - `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3211,8 +3266,10 @@ resource "azurerm_subnet" "test1" { resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name address_prefixes = ["10.0.1.0/24"] + delegation { name = "delegation" + service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3225,8 +3282,10 @@ resource "azurerm_subnet" "test2" { 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"] @@ -3244,7 +3303,6 @@ resource "azurerm_windows_function_app" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} } - `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3273,6 +3331,148 @@ resource "azurerm_windows_function_app" "test" { vnet_route_all_enabled = true } } - `, ServicePlanResource{}.aseV3(data), data.RandomString, data.RandomInteger) } + +func (r WindowsFunctionAppResource) withStorageAccount(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_windows_function_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + site_config {} + + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/files" + } + +} +`, r.templateWithStorageAccount(data), data.RandomInteger) +} + +func (r WindowsFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_windows_function_app" "test" { + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + + site_config {} + + storage_account { + name = "updatedfiles" + type = "AzureBlob" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/blob" + } + +} +`, r.templateWithStorageAccount(data), data.RandomInteger) +} + +func (r WindowsFunctionAppResource) templateWithStorageAccount(data acceptance.TestData) string { + return fmt.Sprintf(` + +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +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_storage_container" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + + resource_types { + service = false + container = false + object = true + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-01" + expiry = "2024-03-30" + + permissions { + read = false + write = true + delete = false + list = false + add = false + create = false + update = false + process = false + tag = false + filter = false + } +} +`, r.standardPlanTemplate(data), data.RandomInteger, data.RandomString) +} + +func (WindowsFunctionAppResource) standardPlanTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_service_plan" "test" { + name = "acctestASP-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + os_type = "Windows" + sku_name = "S1" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} From 7ba91a38d7dfb2e1a0f12b7846f933256ccc7b60 Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Fri, 6 May 2022 14:31:54 +0200 Subject: [PATCH 02/11] fix more copy paste errors (windows vs linux) --- .../services/appservice/linux_function_app_resource.go | 2 +- .../services/appservice/windows_function_app_resource.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource.go b/internal/services/appservice/linux_function_app_resource.go index 3880aa8c9ec8..2b7654b492fd 100644 --- a/internal/services/appservice/linux_function_app_resource.go +++ b/internal/services/appservice/linux_function_app_resource.go @@ -496,7 +496,7 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { SlotConfigNames: stickySettings, } if _, err := client.UpdateSlotConfigurationNames(ctx, id.ResourceGroup, id.SiteName, stickySettingsUpdate); err != nil { - return fmt.Errorf("updating Sticky Settings for Windows %s: %+v", id, err) + return fmt.Errorf("updating Sticky Settings for Linux %s: %+v", id, err) } } diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index 01a9ebd09224..121c98f2d474 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -577,12 +577,12 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { stickySettings, err := client.ListSlotConfigurationNames(ctx, id.ResourceGroup, id.SiteName) if err != nil { - return fmt.Errorf("reading Sticky Settings for Linux %s: %+v", id, err) + return fmt.Errorf("reading Sticky Settings for Windows %s: %+v", id, err) } storageAccounts, err := client.ListAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName) if err != nil { - return fmt.Errorf("reading Storage Account information for Linux %s: %+v", id, err) + return fmt.Errorf("reading Storage Account information for WIndows %s: %+v", id, err) } siteCredentialsFuture, err := client.ListPublishingCredentials(ctx, id.ResourceGroup, id.SiteName) @@ -799,7 +799,7 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { if metadata.ResourceData.HasChange("storage_account") { storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) if _, err := client.UpdateAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName, *storageAccountUpdate); err != nil { - return fmt.Errorf("updating Storage Accounts for Linux %s: %+v", id, err) + return fmt.Errorf("updating Storage Accounts for Windows %s: %+v", id, err) } } @@ -892,7 +892,7 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { } if _, err := client.UpdateSlotConfigurationNames(ctx, id.ResourceGroup, id.SiteName, stickySettingsUpdate); err != nil { - return fmt.Errorf("updating Sticky Settings for Linux %s: %+v", id, err) + return fmt.Errorf("updating Sticky Settings for Windows %s: %+v", id, err) } } From 9c8b7d348df1bbc42145e89ea48269ebd2b4bc04 Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Thu, 19 May 2022 10:29:28 +0200 Subject: [PATCH 03/11] add documentation for storage_account --- .../docs/r/linux_function_app.html.markdown | 18 ++++++++++++++++++ .../docs/r/windows_function_app.html.markdown | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index bfc3b6338c4a..a4509533f29c 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -102,6 +102,8 @@ The following arguments are supported: * `key_vault_reference_identity_id` - (Optional) The User Assigned Identity ID used for accessing KeyVault secrets. The identity must be assigned to the application in the `identity` block. [For more information see - Access vaults with a user-assigned identity](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references#access-vaults-with-a-user-assigned-identity) +* `storage_account` - (Optional) One or more `storage_account` blocks as defined below. + * `sticky_settings` - A `sticky_settings` block as defined below. * `storage_account_access_key` - (Optional) The access key which will be used to access the backend storage account for the Function App. Conflicts with `storage_uses_managed_identity`. @@ -469,6 +471,22 @@ A `sticky_settings` block supports the following: --- +A `storage_account` block supports the following: + +* `access_key` - (Required) The Access key for the storage account. + +* `account_name` - (Required) The Name of the Storage Account. + +* `name` - (Required) The name which should be used for this Storage Account. + +* `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. + +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob` + +* `mount_path` - (Optional) The path at which to mount the storage share. + +--- + A `twitter` block supports the following: * `consumer_key` - (Required) The OAuth 1.0a consumer key of the Twitter application used for sign-in. diff --git a/website/docs/r/windows_function_app.html.markdown b/website/docs/r/windows_function_app.html.markdown index 1298bfd0624f..5d7c12ab5629 100644 --- a/website/docs/r/windows_function_app.html.markdown +++ b/website/docs/r/windows_function_app.html.markdown @@ -102,6 +102,8 @@ The following arguments are supported: * `key_vault_reference_identity_id` - (Optional) The User Assigned Identity ID used for accessing KeyVault secrets. The identity must be assigned to the application in the `identity` block. [For more information see - Access vaults with a user-assigned identity](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references#access-vaults-with-a-user-assigned-identity) +* `storage_account` - (Optional) One or more `storage_account` blocks as defined below. + * `sticky_settings` - A `sticky_settings` block as defined below. * `storage_account_access_key` - (Optional) The access key which will be used to access the backend storage account for the Function App. Conflicts with `storage_uses_managed_identity`. @@ -443,6 +445,22 @@ A `sticky_settings` block exports the following: --- +A `storage_account` block supports the following: + +* `access_key` - (Required) The Access key for the storage account. + +* `account_name` - (Required) The Name of the Storage Account. + +* `name` - (Required) The name which should be used for this Storage Account. + +* `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. + +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob` + +* `mount_path` - (Optional) The path at which to mount the storage share. + +--- + A `twitter` block supports the following: * `consumer_key` - (Required) The OAuth 1.0a consumer key of the Twitter application used for sign-in. From 882b8665ad0d3936e7e2d1a0e3b1b29c03694a14 Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Tue, 24 May 2022 10:29:26 +0200 Subject: [PATCH 04/11] fix r.basic calls in *_function_app_resource_test.go --- .../services/appservice/linux_function_app_resource_test.go | 4 ++-- .../services/appservice/windows_function_app_resource_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 2efe6562ac4c..08487869edd5 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -556,7 +556,7 @@ func TestAccLinuxFunctionApp_withStorageAccountUpdate(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.basic(data, SkuConsumptionPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -577,7 +577,7 @@ func TestAccLinuxFunctionApp_withStorageAccountUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.basic(data), + Config: r.basic(data, SkuConsumptionPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index a59278c60d5e..7442cf601cc8 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -548,7 +548,7 @@ func TestAccWindowsFunctionApp_withStorageAccountUpdate(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.basic(data, SkuConsumptionPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -569,7 +569,7 @@ func TestAccWindowsFunctionApp_withStorageAccountUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.basic(data), + Config: r.basic(data, SkuConsumptionPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), From 5f6f527f3d758cac319c643491407c8141218f3b Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Tue, 14 Jun 2022 15:14:15 +0200 Subject: [PATCH 05/11] [linux|windows]_function_app_resource_test: specify storage_account_name in withStorageAccount and withStorageAccountUpdate methods. --- .../linux_function_app_resource_test.go | 35 +++++++++++++++---- .../windows_function_app_resource_test.go | 20 ++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 08487869edd5..346abe94188c 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -3448,20 +3448,25 @@ func (r LinuxFunctionAppResource) vNetIntegration_basic(data acceptance.TestData 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"] @@ -3495,7 +3500,10 @@ resource "azurerm_linux_function_app" "test" { storage_account_access_key = azurerm_storage_account.test.primary_access_key site_config {} + } + + `, r.template(data, planSku), data.RandomInteger, data.RandomInteger) } @@ -3504,7 +3512,9 @@ func (r LinuxFunctionAppResource) vNetIntegration_subnet1(data acceptance.TestDa provider "azurerm" { features {} } + %s + resource "azurerm_virtual_network" "test" { name = "vnet-%d" address_space = ["10.0.0.0/16"] @@ -3520,6 +3530,7 @@ resource "azurerm_subnet" "test1" { delegation { name = "delegation" + service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3535,18 +3546,21 @@ resource "azurerm_subnet" "test2" { 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 @@ -3560,20 +3574,25 @@ func (r LinuxFunctionAppResource) vNetIntegration_subnet2(data acceptance.TestDa 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"] @@ -3589,6 +3608,7 @@ resource "azurerm_subnet" "test2" { delegation { name = "delegation" + service_delegation { name = "Microsoft.Web/serverFarms" actions = ["Microsoft.Network/virtualNetworks/subnets/action"] @@ -3669,15 +3689,17 @@ func (r LinuxFunctionAppResource) withStorageAccountUpdate(data acceptance.TestD provider "azurerm" { features {} } + %s + resource "azurerm_linux_function_app" "test" { - name = "acctestWA-%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 + name = "acctestWA-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + service_plan_id = azurerm_service_plan.test.id + site_config {} + storage_account { name = "updatedfiles" type = "AzureBlob" @@ -3686,6 +3708,7 @@ resource "azurerm_linux_function_app" "test" { access_key = azurerm_storage_account.test.primary_access_key mount_path = "/blob" } + } `, r.templateWithStorageAccount(data), data.RandomInteger) } diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index 7442cf601cc8..5d88efc3024a 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -3343,10 +3343,12 @@ provider "azurerm" { %s resource "azurerm_windows_function_app" "test" { - name = "acctestWA-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - service_plan_id = azurerm_service_plan.test.id + name = "acctestWA-%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 site_config {} @@ -3372,10 +3374,12 @@ provider "azurerm" { %s resource "azurerm_windows_function_app" "test" { - name = "acctestWA-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - service_plan_id = azurerm_service_plan.test.id + name = "acctestWA-%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 site_config {} From c27ccdaa96f7d7c0d78b2c980d595f0cd48b55af Mon Sep 17 00:00:00 2001 From: Benedikt Fuchs Date: Mon, 4 Jul 2022 09:53:44 +0200 Subject: [PATCH 06/11] windows_function_app_resource_test use mount_path the same way as in windows_web_app_resource_test.go --- .../services/appservice/windows_function_app_resource_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index 5d88efc3024a..384b4debc5ac 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -3358,7 +3358,7 @@ resource "azurerm_windows_function_app" "test" { account_name = azurerm_storage_account.test.name share_name = azurerm_storage_share.test.name access_key = azurerm_storage_account.test.primary_access_key - mount_path = "/files" + mount_path = "\\mounts\\files" } } @@ -3389,7 +3389,7 @@ resource "azurerm_windows_function_app" "test" { account_name = azurerm_storage_account.test.name share_name = azurerm_storage_share.test.name access_key = azurerm_storage_account.test.primary_access_key - mount_path = "/blob" + mount_path = "\\mounts\\blob" } } From 6c2bd055511b06e8e4e147205814629109d7e75f Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 13 Oct 2022 10:08:31 +0100 Subject: [PATCH 07/11] fixup tests for sku --- .../linux_function_app_resource_test.go | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 346abe94188c..162801e2b676 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -535,13 +535,13 @@ func TestAccLinuxFunctionApp_withAuthSettingsStandard(t *testing.T) { }) } -func TestAccLinuxFunctionApp_withStorageAccount(t *testing.T) { +func TestAccLinuxFunctionApp_withStorageAccountBlock(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.withStorageAccount(data), + Config: r.withStorageAccount(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -550,34 +550,34 @@ func TestAccLinuxFunctionApp_withStorageAccount(t *testing.T) { }) } -func TestAccLinuxFunctionApp_withStorageAccountUpdate(t *testing.T) { +func TestAccLinuxFunctionApp_withStorageAccountBlockUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") r := LinuxFunctionAppResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data, SkuConsumptionPlan), + Config: r.basic(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.withStorageAccount(data), + Config: r.withStorageAccount(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.withStorageAccountUpdate(data), + Config: r.withStorageAccountUpdate(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.basic(data, SkuConsumptionPlan), + Config: r.basic(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -3658,12 +3658,14 @@ resource "azurerm_linux_function_app" "test" { `, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) } -func (r LinuxFunctionAppResource) withStorageAccount(data acceptance.TestData) string { +func (r LinuxFunctionAppResource) withStorageAccount(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} } + %s + resource "azurerm_linux_function_app" "test" { name = "acctestWA-%d" location = azurerm_resource_group.test.location @@ -3671,7 +3673,9 @@ resource "azurerm_linux_function_app" "test" { 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 + site_config {} + storage_account { name = "files" type = "AzureFiles" @@ -3681,10 +3685,10 @@ resource "azurerm_linux_function_app" "test" { mount_path = "/files" } } -`, r.templateWithStorageAccount(data), data.RandomInteger) +`, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } -func (r LinuxFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData) string { +func (r LinuxFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3710,34 +3714,31 @@ resource "azurerm_linux_function_app" "test" { } } -`, r.templateWithStorageAccount(data), data.RandomInteger) +`, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } -func (r LinuxFunctionAppResource) templateWithStorageAccount(data acceptance.TestData) string { +func (r LinuxFunctionAppResource) templateWithStorageAccountExtras(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` %s + resource "azurerm_user_assigned_identity" "test" { name = "acct-%d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location } -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_storage_container" "test" { name = "test" storage_account_name = azurerm_storage_account.test.name container_access_type = "private" } + resource "azurerm_storage_share" "test" { name = "test" storage_account_name = azurerm_storage_account.test.name quota = 1 } + data "azurerm_storage_account_sas" "test" { connection_string = azurerm_storage_account.test.primary_connection_string https_only = true @@ -3767,21 +3768,5 @@ data "azurerm_storage_account_sas" "test" { filter = false } } -`, r.standardPlanTemplate(data), data.RandomInteger, data.RandomString) -} - -func (LinuxFunctionAppResource) standardPlanTemplate(data acceptance.TestData) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} -resource "azurerm_service_plan" "test" { - name = "acctestASP-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - os_type = "Linux" - sku_name = "S1" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, r.template(data, planSKU), data.RandomInteger) } From 39ee1072191476c85167178c91b16144335b9b83 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 13 Oct 2022 10:34:37 +0100 Subject: [PATCH 08/11] fix update test config --- .../services/appservice/linux_function_app_resource_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index 162801e2b676..fc3f2cfbbab1 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -3702,6 +3702,9 @@ resource "azurerm_linux_function_app" "test" { 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 + site_config {} storage_account { From c7124fc8b29e34c4e961fed79c2f15d85393bae2 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 13 Oct 2022 15:24:54 +0100 Subject: [PATCH 09/11] Add windows specific schema (doesn't allow Blobs) Update / rationalise tests fixup various items --- .../appservice/helpers/web_app_schema.go | 48 ++++ .../linux_function_app_resource_test.go | 67 ++++-- .../linux_function_app_slot_resource.go | 50 +++- .../linux_function_app_slot_resource_test.go | 197 ++++++++++++++++ .../windows_function_app_resource.go | 4 +- .../windows_function_app_resource_test.go | 101 +++++---- .../windows_function_app_slot_resource.go | 26 +++ ...windows_function_app_slot_resource_test.go | 213 +++++++++++++++++- 8 files changed, 635 insertions(+), 71 deletions(-) diff --git a/internal/services/appservice/helpers/web_app_schema.go b/internal/services/appservice/helpers/web_app_schema.go index a88cee4e4483..ca850a0883fd 100644 --- a/internal/services/appservice/helpers/web_app_schema.go +++ b/internal/services/appservice/helpers/web_app_schema.go @@ -2229,6 +2229,54 @@ func StorageAccountSchema() *pluginsdk.Schema { } } +func AzureStorageAccountSchemaWindows() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(web.AzureStorageTypeAzureFiles), // Note: Windows Apps only support AzureFiles for AzureStorageAccount type. + }, false), + }, + + "account_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "share_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "access_key": { + Type: pluginsdk.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "mount_path": { + Type: pluginsdk.TypeString, + Optional: true, + }, + }, + }, + } +} + func StorageAccountSchemaWindows() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeSet, diff --git a/internal/services/appservice/linux_function_app_resource_test.go b/internal/services/appservice/linux_function_app_resource_test.go index fc3f2cfbbab1..587404fcc125 100644 --- a/internal/services/appservice/linux_function_app_resource_test.go +++ b/internal/services/appservice/linux_function_app_resource_test.go @@ -541,7 +541,22 @@ func TestAccLinuxFunctionApp_withStorageAccountBlock(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.withStorageAccount(data, SkuStandardPlan), + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionApp_withStorageAccountBlocks(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") + r := LinuxFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -563,14 +578,21 @@ func TestAccLinuxFunctionApp_withStorageAccountBlockUpdate(t *testing.T) { }, data.ImportStep(), { - Config: r.withStorageAccount(data, SkuStandardPlan), + Config: r.withStorageAccountSingle(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.withStorageAccountUpdate(data, SkuStandardPlan), + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -3658,7 +3680,7 @@ resource "azurerm_linux_function_app" "test" { `, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) } -func (r LinuxFunctionAppResource) withStorageAccount(data acceptance.TestData, planSKU string) string { +func (r LinuxFunctionAppResource) withStorageAccountSingle(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3688,7 +3710,7 @@ resource "azurerm_linux_function_app" "test" { `, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } -func (r LinuxFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData, planSKU string) string { +func (r LinuxFunctionAppResource) withStorageAccountMultiple(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3697,25 +3719,32 @@ provider "azurerm" { %s resource "azurerm_linux_function_app" "test" { - name = "acctestWA-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - service_plan_id = azurerm_service_plan.test.id - + name = "acctestWA-%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 site_config {} storage_account { - name = "updatedfiles" - type = "AzureBlob" + name = "files" + type = "AzureFiles" account_name = azurerm_storage_account.test.name share_name = azurerm_storage_share.test.name access_key = azurerm_storage_account.test.primary_access_key - mount_path = "/blob" + mount_path = "/files" } + storage_account { + name = "blobs" + type = "AzureBlob" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test2.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/blob" + } } `, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } @@ -3742,6 +3771,18 @@ resource "azurerm_storage_share" "test" { quota = 1 } +resource "azurerm_storage_container" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + data "azurerm_storage_account_sas" "test" { connection_string = azurerm_storage_account.test.primary_connection_string https_only = true diff --git a/internal/services/appservice/linux_function_app_slot_resource.go b/internal/services/appservice/linux_function_app_slot_resource.go index cfc8bd8e92db..31cf2a7d0255 100644 --- a/internal/services/appservice/linux_function_app_slot_resource.go +++ b/internal/services/appservice/linux_function_app_slot_resource.go @@ -58,6 +58,7 @@ type LinuxFunctionAppSlotModel struct { PossibleOutboundIPAddresses string `tfschema:"possible_outbound_ip_addresses"` PossibleOutboundIPAddressList []string `tfschema:"possible_outbound_ip_address_list"` SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` + StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` } var _ sdk.ResourceWithUpdate = LinuxFunctionAppSlotResource{} @@ -232,6 +233,8 @@ func (r LinuxFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema { "site_config": helpers.SiteConfigSchemaLinuxFunctionAppSlot(), + "storage_account": helpers.StorageAccountSchema(), + "tags": tags.Schema(), "virtual_network_subnet_id": { @@ -502,6 +505,15 @@ func (r LinuxFunctionAppSlotResource) Create() sdk.ResourceFunc { } } + storageConfig := helpers.ExpandStorageConfig(functionAppSlot.StorageAccounts) + if storageConfig.Properties != nil { + if _, err := client.UpdateAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, *storageConfig, id.SlotName); err != nil { + if err != nil { + return fmt.Errorf("setting Storage Accounts for Linux %s: %+v", id, err) + } + } + } + if _, ok := metadata.ResourceData.GetOk("site_config.0.app_service_logs"); ok { appServiceLogs := helpers.ExpandFunctionAppAppServiceLogs(functionAppSlot.SiteConfig[0].AppServiceLogs) if _, err := client.UpdateDiagnosticLogsConfigSlot(ctx, id.ResourceGroup, id.SiteName, appServiceLogs, id.SlotName); err != nil { @@ -524,18 +536,18 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { if err != nil { return err } - functionApp, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) + functionAppSlot, err := client.GetSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { - if utils.ResponseWasNotFound(functionApp.Response) { + if utils.ResponseWasNotFound(functionAppSlot.Response) { return metadata.MarkAsGone(id) } return fmt.Errorf("reading Linux %s: %+v", id, err) } - if functionApp.SiteProperties == nil { + if functionAppSlot.SiteProperties == nil { return fmt.Errorf("reading properties of Linux %s", id) } - props := *functionApp.SiteProperties + props := *functionAppSlot.SiteProperties appSettingsResp, err := client.ListApplicationSettingsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { @@ -572,6 +584,11 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { } } + storageAccounts, err := client.ListAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) + if err != nil { + return fmt.Errorf("reading Storage Account information for Linux %s: %+v", id, err) + } + logs, err := client.GetDiagnosticLogsConfigurationSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { return fmt.Errorf("reading logs configuration for Linux %s: %+v", id, err) @@ -580,12 +597,12 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { state := LinuxFunctionAppSlotModel{ Name: id.SlotName, FunctionAppID: parse.NewFunctionAppID(id.SubscriptionId, id.ResourceGroup, id.SiteName).ID(), - Enabled: utils.NormaliseNilableBool(functionApp.Enabled), - ClientCertMode: string(functionApp.ClientCertMode), - ClientCertExclusionPaths: utils.NormalizeNilableString(functionApp.ClientCertExclusionPaths), + Enabled: utils.NormaliseNilableBool(functionAppSlot.Enabled), + ClientCertMode: string(functionAppSlot.ClientCertMode), + ClientCertExclusionPaths: utils.NormalizeNilableString(functionAppSlot.ClientCertExclusionPaths), DailyMemoryTimeQuota: int(utils.NormaliseNilableInt32(props.DailyMemoryTimeQuota)), - Tags: tags.ToTypedObject(functionApp.Tags), - Kind: utils.NormalizeNilableString(functionApp.Kind), + Tags: tags.ToTypedObject(functionAppSlot.Tags), + Kind: utils.NormalizeNilableString(functionAppSlot.Kind), KeyVaultReferenceIdentityID: utils.NormalizeNilableString(props.KeyVaultReferenceIdentity), CustomDomainVerificationId: utils.NormalizeNilableString(props.CustomDomainVerificationID), DefaultHostname: utils.NormalizeNilableString(props.DefaultHostName), @@ -614,8 +631,10 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { state.SiteConfig[0].AppServiceLogs = helpers.FlattenFunctionAppAppServiceLogs(logs) - state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) - state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) + state.StorageAccounts = helpers.FlattenStorageAccounts(storageAccounts) + + state.HttpsOnly = utils.NormaliseNilableBool(functionAppSlot.HTTPSOnly) + state.ClientCertEnabled = utils.NormaliseNilableBool(functionAppSlot.ClientCertEnabled) if subnetId := utils.NormalizeNilableString(props.VirtualNetworkSubnetID); subnetId != "" { state.VirtualNetworkSubnetID = subnetId @@ -625,7 +644,7 @@ func (r LinuxFunctionAppSlotResource) Read() sdk.ResourceFunc { return fmt.Errorf("encoding: %+v", err) } - flattenedIdentity, err := flattenIdentity(functionApp.Identity) + flattenedIdentity, err := flattenIdentity(functionAppSlot.Identity) if err != nil { return fmt.Errorf("flattening `identity`: %+v", err) } @@ -746,6 +765,13 @@ func (r LinuxFunctionAppSlotResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("storage_account") { + storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) + if _, err := client.UpdateAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, *storageAccountUpdate, id.SlotName); err != nil { + return fmt.Errorf("updating Storage Accounts for Linux %s: %+v", id, err) + } + } + if sendContentSettings { appSettingsResp, err := client.ListApplicationSettingsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != 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 f21f556fb046..3f5393b2d49b 100644 --- a/internal/services/appservice/linux_function_app_slot_resource_test.go +++ b/internal/services/appservice/linux_function_app_slot_resource_test.go @@ -1052,6 +1052,79 @@ func TestAccLinuxFunctionAppSlotASEv3_basic(t *testing.T) { }) } +func TestAccLinuxFunctionAppSlot_withStorageAccountBlock(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionAppSlot_withStorageAccountBlocks(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccLinuxFunctionAppSlot_withStorageAccountBlockUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_linux_function_app_slot", "test") + r := LinuxFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // Configs func (r LinuxFunctionAppSlotResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -2882,3 +2955,127 @@ resource "azurerm_linux_function_app_slot" "test" { `, ServicePlanResource{}.aseV3Linux(data), data.RandomString, data.RandomInteger) } + +func (r LinuxFunctionAppSlotResource) withStorageAccountSingle(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 + + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/storage/files" + } + + site_config {} +} +`, r.templateWithStorageAccountExtras(data, planSku), data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) withStorageAccountMultiple(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 + + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/storage/files" + } + + storage_account { + name = "blobs" + type = "AzureBlob" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "/storage/blobs" + } + + site_config {} +} +`, r.templateWithStorageAccountExtras(data, planSku), data.RandomInteger) +} + +func (r LinuxFunctionAppSlotResource) templateWithStorageAccountExtras(data acceptance.TestData, planSKU string) string { + return fmt.Sprintf(` + +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_storage_container" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + + resource_types { + service = false + container = false + object = true + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-01" + expiry = "2024-03-30" + + permissions { + read = false + write = true + delete = false + list = false + add = false + create = false + update = false + process = false + tag = false + filter = false + } +} +`, r.template(data, planSKU), data.RandomInteger) +} diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index 121c98f2d474..b175ffb12dbc 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -252,7 +252,7 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), - "storage_account": helpers.StorageAccountSchema(), + "storage_account": helpers.AzureStorageAccountSchemaWindows(), "tags": tags.Schema(), @@ -582,7 +582,7 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { storageAccounts, err := client.ListAzureStorageAccounts(ctx, id.ResourceGroup, id.SiteName) if err != nil { - return fmt.Errorf("reading Storage Account information for WIndows %s: %+v", id, err) + return fmt.Errorf("reading Storage Account information for Windows %s: %+v", id, err) } siteCredentialsFuture, err := client.ListPublishingCredentials(ctx, id.ResourceGroup, id.SiteName) diff --git a/internal/services/appservice/windows_function_app_resource_test.go b/internal/services/appservice/windows_function_app_resource_test.go index 384b4debc5ac..3481a90e3667 100644 --- a/internal/services/appservice/windows_function_app_resource_test.go +++ b/internal/services/appservice/windows_function_app_resource_test.go @@ -527,13 +527,13 @@ func TestAccWindowsFunctionApp_withAuthSettingsStandard(t *testing.T) { }) } -func TestAccWindowsFunctionApp_withStorageAccount(t *testing.T) { +func TestAccWindowsFunctionApp_withStorageAccountBlock(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.withStorageAccount(data), + Config: r.withStorageAccountSingle(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -542,34 +542,56 @@ func TestAccWindowsFunctionApp_withStorageAccount(t *testing.T) { }) } -func TestAccWindowsFunctionApp_withStorageAccountUpdate(t *testing.T) { +func TestAccWindowsFunctionApp_withStorageAccountBlocks(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") r := WindowsFunctionAppResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data, SkuConsumptionPlan), + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionApp_withStorageAccountBlockUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app", "test") + r := WindowsFunctionAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.withStorageAccount(data), + Config: r.withStorageAccountSingle(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.withStorageAccountUpdate(data), + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.basic(data, SkuConsumptionPlan), + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data, SkuStandardPlan), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -3334,7 +3356,7 @@ resource "azurerm_windows_function_app" "test" { `, ServicePlanResource{}.aseV3(data), data.RandomString, data.RandomInteger) } -func (r WindowsFunctionAppResource) withStorageAccount(data acceptance.TestData) string { +func (r WindowsFunctionAppResource) withStorageAccountSingle(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3362,10 +3384,10 @@ resource "azurerm_windows_function_app" "test" { } } -`, r.templateWithStorageAccount(data), data.RandomInteger) +`, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } -func (r WindowsFunctionAppResource) withStorageAccountUpdate(data acceptance.TestData) string { +func (r WindowsFunctionAppResource) withStorageAccountMultiple(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -3384,19 +3406,28 @@ resource "azurerm_windows_function_app" "test" { site_config {} storage_account { - name = "updatedfiles" - type = "AzureBlob" + name = "files" + type = "AzureFiles" account_name = azurerm_storage_account.test.name share_name = azurerm_storage_share.test.name access_key = azurerm_storage_account.test.primary_access_key - mount_path = "\\mounts\\blob" + mount_path = "\\mounts\\files" + } + + storage_account { + name = "morefiles" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test2.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "\\mounts\\morefiles" } } -`, r.templateWithStorageAccount(data), data.RandomInteger) +`, r.templateWithStorageAccountExtras(data, planSKU), data.RandomInteger) } -func (r WindowsFunctionAppResource) templateWithStorageAccount(data acceptance.TestData) string { +func (r WindowsFunctionAppResource) templateWithStorageAccountExtras(data acceptance.TestData, planSKU string) string { return fmt.Sprintf(` %s @@ -3407,14 +3438,6 @@ resource "azurerm_user_assigned_identity" "test" { location = azurerm_resource_group.test.location } -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_storage_container" "test" { name = "test" storage_account_name = azurerm_storage_account.test.name @@ -3427,6 +3450,18 @@ resource "azurerm_storage_share" "test" { quota = 1 } +resource "azurerm_storage_container" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + data "azurerm_storage_account_sas" "test" { connection_string = azurerm_storage_account.test.primary_connection_string https_only = true @@ -3460,23 +3495,5 @@ data "azurerm_storage_account_sas" "test" { filter = false } } -`, r.standardPlanTemplate(data), data.RandomInteger, data.RandomString) -} - -func (WindowsFunctionAppResource) standardPlanTemplate(data acceptance.TestData) string { - return fmt.Sprintf(` - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_service_plan" "test" { - name = "acctestASP-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - os_type = "Windows" - sku_name = "S1" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, r.template(data, planSKU), data.RandomInteger) } diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index aaace3101bc2..01617cdd60c9 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -57,6 +57,7 @@ type WindowsFunctionAppSlotModel struct { PossibleOutboundIPAddresses string `tfschema:"possible_outbound_ip_addresses"` PossibleOutboundIPAddressList []string `tfschema:"possible_outbound_ip_address_list"` SiteCredentials []helpers.SiteCredential `tfschema:"site_credential"` + StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"` VirtualNetworkSubnetID string `tfschema:"virtual_network_subnet_id"` } @@ -232,6 +233,8 @@ func (r WindowsFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema "site_config": helpers.SiteConfigSchemaWindowsFunctionAppSlot(), + "storage_account": helpers.AzureStorageAccountSchemaWindows(), + "tags": tags.Schema(), "virtual_network_subnet_id": { @@ -504,6 +507,15 @@ func (r WindowsFunctionAppSlotResource) Create() sdk.ResourceFunc { } } + storageConfig := helpers.ExpandStorageConfig(functionAppSlot.StorageAccounts) + if storageConfig.Properties != nil { + if _, err := client.UpdateAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, *storageConfig, id.SlotName); err != nil { + if err != nil { + return fmt.Errorf("setting Storage Accounts for Windows %s: %+v", id, err) + } + } + } + connectionStrings := helpers.ExpandConnectionStrings(functionAppSlot.ConnectionStrings) if connectionStrings.Properties != nil { if _, err := client.UpdateConnectionStringsSlot(ctx, id.ResourceGroup, id.SiteName, *connectionStrings, id.SlotName); err != nil { @@ -556,6 +568,11 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { return fmt.Errorf("reading Connection String information for Windows %s: %+v", id, err) } + storageAccounts, err := client.ListAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) + if err != nil { + return fmt.Errorf("reading Storage Account information for Windows %s: %+v", id, err) + } + siteCredentialsFuture, err := client.ListPublishingCredentialsSlot(ctx, id.ResourceGroup, id.SiteName, id.SlotName) if err != nil { return fmt.Errorf("listing Site Publishing Credential information for Windows %s: %+v", id, err) @@ -623,6 +640,8 @@ func (r WindowsFunctionAppSlotResource) Read() sdk.ResourceFunc { state.SiteConfig[0].AppServiceLogs = helpers.FlattenFunctionAppAppServiceLogs(logs) + state.StorageAccounts = helpers.FlattenStorageAccounts(storageAccounts) + state.HttpsOnly = utils.NormaliseNilableBool(functionApp.HTTPSOnly) state.ClientCertEnabled = utils.NormaliseNilableBool(functionApp.ClientCertEnabled) @@ -748,6 +767,13 @@ func (r WindowsFunctionAppSlotResource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("storage_account") { + storageAccountUpdate := helpers.ExpandStorageConfig(state.StorageAccounts) + if _, err := client.UpdateAzureStorageAccountsSlot(ctx, id.ResourceGroup, id.SiteName, *storageAccountUpdate, id.SlotName); err != nil { + return fmt.Errorf("updating Storage Accounts for Windows %s: %+v", id, err) + } + } + storageString := state.StorageAccountName if !state.StorageUsesMSI { if state.StorageKeyVaultSecretID != "" { 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 d8c6de553727..4dbb8b9da507 100644 --- a/internal/services/appservice/windows_function_app_slot_resource_test.go +++ b/internal/services/appservice/windows_function_app_slot_resource_test.go @@ -98,8 +98,6 @@ func TestAccWindowsFunctionAppSlot_basicStandardPlan(t *testing.T) { }) } -// App Settings by Plan Type - func TestAccWindowsFunctionAppSlot_withAppSettingsConsumption(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") r := WindowsFunctionAppSlotResource{} @@ -117,6 +115,8 @@ func TestAccWindowsFunctionAppSlot_withAppSettingsConsumption(t *testing.T) { }) } +// App Settings by Plan Type + func TestAccWindowsFunctionAppSlot_withAppSettingsElasticPremiumPlan(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") r := WindowsFunctionAppSlotResource{} @@ -986,6 +986,79 @@ func TestAccWindowsFunctionAppSlotASEv3_basic(t *testing.T) { }) } +func TestAccWindowsFunctionAppSlot_withStorageAccountBlock(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlot_withStorageAccountBlocks(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccWindowsFunctionAppSlot_withStorageAccountBlocksUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_windows_function_app_slot", "test") + r := WindowsFunctionAppSlotResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountMultiple(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.withStorageAccountSingle(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data, SkuStandardPlan), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + // Exists func (r WindowsFunctionAppSlotResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { @@ -2663,3 +2736,139 @@ resource "azurerm_windows_function_app_slot" "test" { `, ServicePlanResource{}.aseV3(data), data.RandomString, data.RandomInteger) } + +func (r WindowsFunctionAppSlotResource) withStorageAccountSingle(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +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 + + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "\\mounts\\files" + } + + site_config {} +} +`, r.templateWithStorageAccountExtras(data, planSku), data.RandomInteger) +} + +func (r WindowsFunctionAppSlotResource) withStorageAccountMultiple(data acceptance.TestData, planSku string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +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 + + storage_account { + name = "files" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "\\mounts\\files" + } + + storage_account { + name = "morefiles" + type = "AzureFiles" + account_name = azurerm_storage_account.test.name + share_name = azurerm_storage_share.test2.name + access_key = azurerm_storage_account.test.primary_access_key + mount_path = "\\mounts\\morefiles" + } + + site_config {} +} +`, r.templateWithStorageAccountExtras(data, planSku), data.RandomInteger) +} + +func (r WindowsFunctionAppSlotResource) templateWithStorageAccountExtras(data acceptance.TestData, planSKU string) string { + return fmt.Sprintf(` + +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acct-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_storage_container" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test" { + name = "test" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + +resource "azurerm_storage_container" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_storage_share" "test2" { + name = "test2" + storage_account_name = azurerm_storage_account.test.name + quota = 1 +} + +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + + resource_types { + service = false + container = false + object = true + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-01" + expiry = "2024-03-30" + + permissions { + read = false + write = true + delete = false + list = false + add = false + create = false + update = false + process = false + tag = false + filter = false + } +} +`, r.template(data, planSKU), data.RandomInteger) +} From 036d019ac5efcf58f4dc26f6447fd3630bcbedfe Mon Sep 17 00:00:00 2001 From: jackofallops Date: Fri, 14 Oct 2022 05:53:34 +0100 Subject: [PATCH 10/11] update docs --- .../docs/r/linux_function_app.html.markdown | 2 +- .../r/linux_function_app_slot.html.markdown | 19 +++++++++++++++++++ .../docs/r/windows_function_app.html.markdown | 2 +- .../r/windows_function_app_slot.html.markdown | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/website/docs/r/linux_function_app.html.markdown b/website/docs/r/linux_function_app.html.markdown index a4509533f29c..e8284873af44 100644 --- a/website/docs/r/linux_function_app.html.markdown +++ b/website/docs/r/linux_function_app.html.markdown @@ -481,7 +481,7 @@ A `storage_account` block supports the following: * `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. -* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob` +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob`. * `mount_path` - (Optional) The path at which to mount the storage share. diff --git a/website/docs/r/linux_function_app_slot.html.markdown b/website/docs/r/linux_function_app_slot.html.markdown index 8c83b188acd6..f111b8b402ae 100644 --- a/website/docs/r/linux_function_app_slot.html.markdown +++ b/website/docs/r/linux_function_app_slot.html.markdown @@ -104,6 +104,8 @@ The following arguments are supported: * `storage_account_name` - (Optional) The backend storage account name which will be used by this Function App Slot. +* `storage_account` - (Optional) One or more `storage_account` blocks as defined below. + * `storage_uses_managed_identity` - (Optional) Should the Function App Slot use its Managed Identity to access storage. ~> **NOTE:** One of `storage_account_access_key` or `storage_uses_managed_identity` must be specified when using `storage_account_name`. @@ -478,6 +480,23 @@ A `scm_ip_restriction` block supports the following: ~> **NOTE:** One and only one of `ip_address`, `service_tag` or `virtual_network_subnet_id` must be specified. +--- + +A `storage_account` block supports the following: + +* `access_key` - (Required) The Access key for the storage account. + +* `account_name` - (Required) The Name of the Storage Account. + +* `name` - (Required) The name which should be used for this Storage Account. + +* `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. + +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob`. + +* `mount_path` - (Optional) The path at which to mount the storage share. + + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: diff --git a/website/docs/r/windows_function_app.html.markdown b/website/docs/r/windows_function_app.html.markdown index 5d7c12ab5629..d5c4d4dd75bc 100644 --- a/website/docs/r/windows_function_app.html.markdown +++ b/website/docs/r/windows_function_app.html.markdown @@ -455,7 +455,7 @@ A `storage_account` block supports the following: * `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. -* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles` and `AzureBlob` +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles`. * `mount_path` - (Optional) The path at which to mount the storage share. diff --git a/website/docs/r/windows_function_app_slot.html.markdown b/website/docs/r/windows_function_app_slot.html.markdown index de9374804913..d7bb3c2d7d05 100644 --- a/website/docs/r/windows_function_app_slot.html.markdown +++ b/website/docs/r/windows_function_app_slot.html.markdown @@ -103,6 +103,8 @@ The following arguments are supported: * `storage_account_name` - (Optional) The backend storage account name which will be used by this Function App Slot. +* `storage_account` - (Optional) One or more `storage_account` blocks as defined below. + * `storage_uses_managed_identity` - (Optional) Should the Function App Slot use its Managed Identity to access storage. ~> **NOTE:** One of `storage_account_access_key` or `storage_uses_managed_identity` must be specified when using `storage_account_name`. @@ -451,6 +453,22 @@ A `headers` block supports the following: * `x_forwarded_host` - (Optional) Specifies a list of Hosts for which matching should be applied. +--- + +A `storage_account` block supports the following: + +* `access_key` - (Required) The Access key for the storage account. + +* `account_name` - (Required) The Name of the Storage Account. + +* `name` - (Required) The name which should be used for this Storage Account. + +* `share_name` - (Required) The Name of the File Share or Container Name for Blob storage. + +* `type` - (Required) The Azure Storage Type. Possible values include `AzureFiles`. + +* `mount_path` - (Optional) The path at which to mount the storage share. + ## Attributes Reference From 7dccf9ddeffcbf1744a9716922a0c64bbe2a8476 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Tue, 18 Oct 2022 11:54:36 +0100 Subject: [PATCH 11/11] remove duplicated schema func --- .../appservice/helpers/web_app_schema.go | 48 ------------------- .../windows_function_app_resource.go | 2 +- .../windows_function_app_slot_resource.go | 2 +- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/internal/services/appservice/helpers/web_app_schema.go b/internal/services/appservice/helpers/web_app_schema.go index ca850a0883fd..a88cee4e4483 100644 --- a/internal/services/appservice/helpers/web_app_schema.go +++ b/internal/services/appservice/helpers/web_app_schema.go @@ -2229,54 +2229,6 @@ func StorageAccountSchema() *pluginsdk.Schema { } } -func AzureStorageAccountSchemaWindows() *pluginsdk.Schema { - return &pluginsdk.Schema{ - Type: pluginsdk.TypeSet, - Optional: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "type": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(web.AzureStorageTypeAzureFiles), // Note: Windows Apps only support AzureFiles for AzureStorageAccount type. - }, false), - }, - - "account_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "share_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "access_key": { - Type: pluginsdk.TypeString, - Required: true, - Sensitive: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "mount_path": { - Type: pluginsdk.TypeString, - Optional: true, - }, - }, - }, - } -} - func StorageAccountSchemaWindows() *pluginsdk.Schema { return &pluginsdk.Schema{ Type: pluginsdk.TypeSet, diff --git a/internal/services/appservice/windows_function_app_resource.go b/internal/services/appservice/windows_function_app_resource.go index b175ffb12dbc..967f672aa965 100644 --- a/internal/services/appservice/windows_function_app_resource.go +++ b/internal/services/appservice/windows_function_app_resource.go @@ -252,7 +252,7 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { "sticky_settings": helpers.StickySettingsSchema(), - "storage_account": helpers.AzureStorageAccountSchemaWindows(), + "storage_account": helpers.StorageAccountSchemaWindows(), "tags": tags.Schema(), diff --git a/internal/services/appservice/windows_function_app_slot_resource.go b/internal/services/appservice/windows_function_app_slot_resource.go index 01617cdd60c9..3c4134b8cadb 100644 --- a/internal/services/appservice/windows_function_app_slot_resource.go +++ b/internal/services/appservice/windows_function_app_slot_resource.go @@ -233,7 +233,7 @@ func (r WindowsFunctionAppSlotResource) Arguments() map[string]*pluginsdk.Schema "site_config": helpers.SiteConfigSchemaWindowsFunctionAppSlot(), - "storage_account": helpers.AzureStorageAccountSchemaWindows(), + "storage_account": helpers.StorageAccountSchemaWindows(), "tags": tags.Schema(),