Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add storage account config to function apps #16684

Closed
30 changes: 28 additions & 2 deletions internal/services/appservice/linux_function_app_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type LinuxFunctionAppModel struct {
BuiltinLogging bool `tfschema:"builtin_logging_enabled"`
ClientCertEnabled bool `tfschema:"client_certificate_enabled"`
ClientCertMode string `tfschema:"client_certificate_mode"`
StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"`
ConnectionStrings []helpers.ConnectionString `tfschema:"connection_string"`
DailyMemoryTimeQuota int `tfschema:"daily_memory_time_quota"` // TODO - Value ignored in for linux apps, even in Consumption plans?
Enabled bool `tfschema:"enabled"`
Expand Down Expand Up @@ -242,6 +243,8 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema {

"sticky_settings": helpers.StickySettingsSchema(),

"storage_account": helpers.StorageAccountSchema(),

"tags": tags.Schema(),
}
}
Expand Down Expand Up @@ -474,7 +477,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)
}
}

Expand All @@ -492,6 +495,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 {
Expand Down Expand Up @@ -549,6 +561,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)
Expand Down Expand Up @@ -616,6 +633,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)

Expand Down Expand Up @@ -724,6 +743,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 != "" {
Expand All @@ -736,7 +762,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)
Expand Down
194 changes: 194 additions & 0 deletions internal/services/appservice/linux_function_app_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,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{}
Expand Down Expand Up @@ -3052,3 +3103,146 @@ resource "azurerm_user_assigned_identity" "test" {
}
`, r.template(data, planSku), 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

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

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)
}
31 changes: 29 additions & 2 deletions internal/services/appservice/windows_function_app_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type WindowsFunctionAppModel struct {
BuiltinLogging bool `tfschema:"builtin_logging_enabled"`
ClientCertEnabled bool `tfschema:"client_certificate_enabled"`
ClientCertMode string `tfschema:"client_certificate_mode"`
StorageAccounts []helpers.StorageAccount `tfschema:"storage_account"`
ConnectionStrings []helpers.ConnectionString `tfschema:"connection_string"`
DailyMemoryTimeQuota int `tfschema:"daily_memory_time_quota"`
Enabled bool `tfschema:"enabled"`
Expand Down Expand Up @@ -242,6 +243,8 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema {

"sticky_settings": helpers.StickySettingsSchema(),

"storage_account": helpers.StorageAccountSchema(),

"tags": tags.Schema(),
}
}
Expand Down Expand Up @@ -391,6 +394,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)
Expand Down Expand Up @@ -490,6 +494,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 {
Expand Down Expand Up @@ -544,7 +557,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 WIndows %s: %+v", id, err)
}

siteCredentialsFuture, err := client.ListPublishingCredentials(ctx, id.ResourceGroup, id.SiteName)
Expand Down Expand Up @@ -614,6 +632,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)

Expand Down Expand Up @@ -720,6 +740,13 @@ func (r WindowsFunctionAppResource) 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 Windows %s: %+v", id, err)
}
}

storageString := state.StorageAccountName
if !state.StorageUsesMSI {
if state.StorageKeyVaultSecretID != "" {
Expand Down Expand Up @@ -809,7 +836,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)
}
}

Expand Down
Loading