Skip to content

Commit

Permalink
Function app consumption plan bug (#1515)
Browse files Browse the repository at this point in the history
* Do not load WEBSITE_CONTENT app settings when on a consumption plan

* Inverse the dynamic plan check, thanks @APErebus

* '%s' -> %q for safety

* Add a nil check

* Added tests to ensure the settings are removed:

```
$ acctests azurerm TestAccAzureRMFunctionApp_basic
=== RUN   TestAccAzureRMFunctionApp_basic
--- PASS: TestAccAzureRMFunctionApp_basic (111.90s)
PASS
ok  	github.com/terraform-providers/terraform-provider-azurerm/azurerm	111.938s
```

```
$ acctests azurerm TestAccAzureRMFunctionApp_consumptionPlan
=== RUN   TestAccAzureRMFunctionApp_consumptionPlan
--- PASS: TestAccAzureRMFunctionApp_consumptionPlan (143.36s)
PASS
ok  	github.com/terraform-providers/terraform-provider-azurerm/azurerm	143.406s
```
  • Loading branch information
ranieuwe authored and tombuildsstuff committed Jul 16, 2018
1 parent a0b3434 commit 6ec6d71
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 7 deletions.
59 changes: 52 additions & 7 deletions azurerm/resource_arm_function_app.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package azurerm

import (
"context"
"fmt"
"log"
"strings"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
Expand Down Expand Up @@ -233,7 +235,13 @@ func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) erro
clientAffinityEnabled := d.Get("client_affinity_enabled").(bool)
httpsOnly := d.Get("https_only").(bool)
tags := d.Get("tags").(map[string]interface{})
basicAppSettings := getBasicFunctionAppAppSettings(d)
appServiceTier, err := getFunctionAppServiceTier(ctx, appServicePlanID, meta)
if err != nil {
return err
}

basicAppSettings := getBasicFunctionAppAppSettings(d, appServiceTier)

siteConfig := expandFunctionAppSiteConfig(d)
siteConfig.AppSettings = &basicAppSettings

Expand Down Expand Up @@ -298,7 +306,13 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro
clientAffinityEnabled := d.Get("client_affinity_enabled").(bool)
httpsOnly := d.Get("https_only").(bool)
tags := d.Get("tags").(map[string]interface{})
basicAppSettings := getBasicFunctionAppAppSettings(d)

appServiceTier, err := getFunctionAppServiceTier(ctx, appServicePlanID, meta)

if err != nil {
return err
}
basicAppSettings := getBasicFunctionAppAppSettings(d, appServiceTier)
siteConfig := expandFunctionAppSiteConfig(d)
siteConfig.AppSettings = &basicAppSettings

Expand Down Expand Up @@ -331,7 +345,7 @@ func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) erro
return err
}

appSettings := expandFunctionAppAppSettings(d)
appSettings := expandFunctionAppAppSettings(d, appServiceTier)
settings := web.StringDictionary{
Properties: appSettings,
}
Expand Down Expand Up @@ -495,7 +509,7 @@ func resourceArmFunctionAppDelete(d *schema.ResourceData, meta interface{}) erro
return nil
}

func getBasicFunctionAppAppSettings(d *schema.ResourceData) []web.NameValuePair {
func getBasicFunctionAppAppSettings(d *schema.ResourceData, appServiceTier string) []web.NameValuePair {
dashboardPropName := "AzureWebJobsDashboard"
storagePropName := "AzureWebJobsStorage"
functionVersionPropName := "FUNCTIONS_EXTENSION_VERSION"
Expand All @@ -506,19 +520,50 @@ func getBasicFunctionAppAppSettings(d *schema.ResourceData) []web.NameValuePair
functionVersion := d.Get("version").(string)
contentShare := d.Get("name").(string) + "-content"

return []web.NameValuePair{
basicSettings := []web.NameValuePair{
{Name: &dashboardPropName, Value: &storageConnection},
{Name: &storagePropName, Value: &storageConnection},
{Name: &functionVersionPropName, Value: &functionVersion},
}

consumptionSettings := []web.NameValuePair{
{Name: &contentSharePropName, Value: &contentShare},
{Name: &contentFileConnStringPropName, Value: &storageConnection},
}

// If the application plan is NOT dynamic (consumption plan), we do NOT want to include WEBSITE_CONTENT components
if !strings.EqualFold(appServiceTier, "dynamic") {
return basicSettings
}
return append(basicSettings, consumptionSettings...)
}

func getFunctionAppServiceTier(ctx context.Context, appServicePlanId string, meta interface{}) (string, error) {
id, err := parseAzureResourceID(appServicePlanId)
if err != nil {
return "", fmt.Errorf("[ERROR] Unable to parse App Service Plan ID %q: %+v", appServicePlanId, err)
}

log.Printf("[DEBUG] Retrieving App Server Plan %s", id.Path["serverfarms"])

appServicePlansClient := meta.(*ArmClient).appServicePlansClient
appServicePlan, err := appServicePlansClient.Get(ctx, id.ResourceGroup, id.Path["serverfarms"])
if err != nil {
return "", fmt.Errorf("[ERROR] Could not retrieve App Service Plan ID %q: %+v", appServicePlanId, err)
}

if sku := appServicePlan.Sku; sku != nil {
if tier := sku.Tier; tier != nil {
return *tier, nil
}
}
return "", fmt.Errorf("No `sku` block was returned for App Service Plan ID %q", appServicePlanId)
}

func expandFunctionAppAppSettings(d *schema.ResourceData) map[string]*string {
func expandFunctionAppAppSettings(d *schema.ResourceData, appServiceTier string) map[string]*string {
output := expandAppServiceAppSettings(d)

basicAppSettings := getBasicFunctionAppAppSettings(d)
basicAppSettings := getBasicFunctionAppAppSettings(d, appServiceTier)
for _, p := range basicAppSettings {
output[*p.Name] = p.Value
}
Expand Down
66 changes: 66 additions & 0 deletions azurerm/resource_arm_function_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestAccAzureRMFunctionApp_basic(t *testing.T) {
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMFunctionAppExists(resourceName),
testCheckAzureRMFunctionAppHasNoContentShare(resourceName),
resource.TestCheckResourceAttr(resourceName, "version", "~1"),
),
},
Expand Down Expand Up @@ -329,6 +330,7 @@ func TestAccAzureRMFunctionApp_consumptionPlan(t *testing.T) {
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMFunctionAppExists(resourceName),
testCheckAzureRMFunctionAppHasContentShare(resourceName),
resource.TestCheckResourceAttr(resourceName, "site_config.0.use_32_bit_worker_process", "true"),
),
},
Expand Down Expand Up @@ -454,6 +456,70 @@ func testCheckAzureRMFunctionAppExists(name string) resource.TestCheckFunc {
}
}

func testCheckAzureRMFunctionAppHasContentShare(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

functionAppName := rs.Primary.Attributes["name"]
resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
if !hasResourceGroup {
return fmt.Errorf("Bad: no resource group found in state for Function App: %s", functionAppName)
}

client := testAccProvider.Meta().(*ArmClient).appServicesClient
ctx := testAccProvider.Meta().(*ArmClient).StopContext

appSettingsResp, err := client.ListApplicationSettings(ctx, resourceGroup, functionAppName)
if err != nil {
return fmt.Errorf("Error making Read request on AzureRM Function App AppSettings %q: %+v", functionAppName, err)
}

for k, _ := range appSettingsResp.Properties {
if strings.EqualFold("WEBSITE_CONTENTSHARE", k) {
return nil
}
}

return fmt.Errorf("Function App %q does not contain the Website Content Share!", functionAppName)
}
}

func testCheckAzureRMFunctionAppHasNoContentShare(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
// Ensure we have enough information in state to look up in API
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

functionAppName := rs.Primary.Attributes["name"]
resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"]
if !hasResourceGroup {
return fmt.Errorf("Bad: no resource group found in state for Function App: %s", functionAppName)
}

client := testAccProvider.Meta().(*ArmClient).appServicesClient
ctx := testAccProvider.Meta().(*ArmClient).StopContext

appSettingsResp, err := client.ListApplicationSettings(ctx, resourceGroup, functionAppName)
if err != nil {
return fmt.Errorf("Error making Read request on AzureRM Function App AppSettings %q: %+v", functionAppName, err)
}

for k, v := range appSettingsResp.Properties {
if strings.EqualFold("WEBSITE_CONTENTSHARE", k) && v != nil && *v != "" {
return fmt.Errorf("Function App %q contains the Website Content Share!", functionAppName)
}
}

return nil
}
}

func testAccAzureRMFunctionApp_basic(rInt int, storage string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down

0 comments on commit 6ec6d71

Please sign in to comment.