-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
New Resource: azurerm_function_app
#647
Changes from 7 commits
d68cf30
1f25bbe
f3509b8
d33cff0
8e92273
5c38bae
eb8a625
ce731b3
a7ccef6
8185aa6
afcdaac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
package azurerm | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/Azure/azure-sdk-for-go/arm/web" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
// Azure Function App shares the same infrastructure with Azure App Service. | ||
// So this resource will reuse most of the App Service code, but remove the configurations which are not applicable for Function App. | ||
func resourceArmFunctionApp() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceArmFunctionAppCreate, | ||
Read: resourceArmFunctionAppRead, | ||
Update: resourceArmFunctionAppUpdate, | ||
Delete: resourceArmFunctionAppDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateAppServiceName, | ||
}, | ||
|
||
"resource_group_name": resourceGroupNameSchema(), | ||
|
||
"location": locationSchema(), | ||
|
||
"app_service_plan_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"enabled": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: true, | ||
|
||
// TODO: (tombuildsstuff) support Update once the API is fixed: | ||
// https://github.com/Azure/azure-rest-api-specs/issues/1697 | ||
ForceNew: true, | ||
}, | ||
|
||
"version": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "~1", | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
"~1", | ||
"beta", | ||
}, false), | ||
}, | ||
|
||
"storage_connection_string": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"app_settings": { | ||
Type: schema.TypeMap, | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
|
||
// TODO: (tombuildsstuff) support Update once the API is fixed: | ||
// https://github.com/Azure/azure-rest-api-specs/issues/1697 | ||
"tags": tagsForceNewSchema(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we link to the existing issue i.e.
|
||
|
||
"default_hostname": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceArmFunctionAppCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).appServicesClient | ||
|
||
log.Printf("[INFO] preparing arguments for AzureRM Function App creation.") | ||
|
||
name := d.Get("name").(string) | ||
resGroup := d.Get("resource_group_name").(string) | ||
location := d.Get("location").(string) | ||
kind := "functionapp" | ||
appServicePlanID := d.Get("app_service_plan_id").(string) | ||
enabled := d.Get("enabled").(bool) | ||
tags := d.Get("tags").(map[string]interface{}) | ||
basicAppSettings := getBasicFunctionAppAppSettings(d) | ||
|
||
siteEnvelope := web.Site{ | ||
Kind: &kind, | ||
Location: &location, | ||
Tags: expandTags(tags), | ||
SiteProperties: &web.SiteProperties{ | ||
ServerFarmID: utils.String(appServicePlanID), | ||
Enabled: utils.Bool(enabled), | ||
SiteConfig: &web.SiteConfig{ | ||
AppSettings: &basicAppSettings, | ||
}, | ||
}, | ||
} | ||
|
||
skipDNSRegistration := false | ||
forceDNSRegistration := false | ||
skipCustomDomainVerification := true | ||
ttlInSeconds := "60" | ||
_, createErr := client.CreateOrUpdate(resGroup, name, siteEnvelope, &skipDNSRegistration, &skipCustomDomainVerification, &forceDNSRegistration, ttlInSeconds, make(chan struct{})) | ||
err := <-createErr | ||
if err != nil { | ||
return err | ||
} | ||
|
||
read, err := client.Get(resGroup, name) | ||
if err != nil { | ||
return err | ||
} | ||
if read.ID == nil { | ||
return fmt.Errorf("Cannot read Function App %s (resource group %s) ID", name, resGroup) | ||
} | ||
|
||
d.SetId(*read.ID) | ||
|
||
return resourceArmFunctionAppUpdate(d, meta) | ||
} | ||
|
||
func resourceArmFunctionAppUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).appServicesClient | ||
|
||
id, err := parseAzureResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resGroup := id.ResourceGroup | ||
name := id.Path["sites"] | ||
|
||
if d.HasChange("app_settings") || d.HasChange("version") { | ||
appSettings := expandFunctionAppAppSettings(d) | ||
settings := web.StringDictionary{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to append the Function App specific app settings to these values before updating? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly! Thanks for finding the bug. |
||
Properties: appSettings, | ||
} | ||
|
||
_, err := client.UpdateApplicationSettings(resGroup, name, settings) | ||
if err != nil { | ||
return fmt.Errorf("Error updating Application Settings for Function App %q: %+v", name, err) | ||
} | ||
} | ||
|
||
return resourceArmFunctionAppRead(d, meta) | ||
} | ||
|
||
func resourceArmFunctionAppRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).appServicesClient | ||
|
||
id, err := parseAzureResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resGroup := id.ResourceGroup | ||
name := id.Path["sites"] | ||
|
||
resp, err := client.Get(resGroup, name) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
log.Printf("[DEBUG] Function App %q (resource group %q) was not found - removing from state", name, resGroup) | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("Error making Read request on AzureRM Function App %q: %+v", name, err) | ||
} | ||
|
||
appSettingsResp, err := client.ListApplicationSettings(resGroup, name) | ||
if err != nil { | ||
return fmt.Errorf("Error making Read request on AzureRM Function App AppSettings %q: %+v", name, err) | ||
} | ||
|
||
d.Set("name", name) | ||
d.Set("resource_group_name", resGroup) | ||
d.Set("location", azureRMNormalizeLocation(*resp.Location)) | ||
|
||
if props := resp.SiteProperties; props != nil { | ||
d.Set("app_service_plan_id", props.ServerFarmID) | ||
d.Set("enabled", props.Enabled) | ||
d.Set("default_hostname", props.DefaultHostName) | ||
} | ||
|
||
appSettings := flattenAppServiceAppSettings(appSettingsResp.Properties) | ||
|
||
d.Set("storage_connection_string", appSettings["AzureWebJobsStorage"]) | ||
d.Set("version", appSettings["FUNCTIONS_EXTENSION_VERSION"]) | ||
|
||
delete(appSettings, "AzureWebJobsDashboard") | ||
delete(appSettings, "AzureWebJobsStorage") | ||
delete(appSettings, "FUNCTIONS_EXTENSION_VERSION") | ||
delete(appSettings, "WEBSITE_CONTENTSHARE") | ||
delete(appSettings, "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING") | ||
|
||
if err := d.Set("app_settings", appSettings); err != nil { | ||
return err | ||
} | ||
|
||
flattenAndSetTags(d, resp.Tags) | ||
|
||
return nil | ||
} | ||
|
||
func resourceArmFunctionAppDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).appServicesClient | ||
|
||
id, err := parseAzureResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
resGroup := id.ResourceGroup | ||
name := id.Path["sites"] | ||
|
||
log.Printf("[DEBUG] Deleting Function App %q (resource group %q)", name, resGroup) | ||
|
||
deleteMetrics := true | ||
deleteEmptyServerFarm := false | ||
skipDNSRegistration := true | ||
resp, err := client.Delete(resGroup, name, &deleteMetrics, &deleteEmptyServerFarm, &skipDNSRegistration) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(resp) { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getBasicFunctionAppAppSettings(d *schema.ResourceData) []web.NameValuePair { | ||
dashboardPropName := "AzureWebJobsDashboard" | ||
storagePropName := "AzureWebJobsStorage" | ||
functionVersionPropName := "FUNCTIONS_EXTENSION_VERSION" | ||
contentSharePropName := "WEBSITE_CONTENTSHARE" | ||
contentFileConnStringPropName := "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" | ||
|
||
storageConnection := d.Get("storage_connection_string").(string) | ||
functionVersion := d.Get("version").(string) | ||
contentShare := d.Get("name").(string) + "-content" | ||
|
||
return []web.NameValuePair{ | ||
{Name: &dashboardPropName, Value: &storageConnection}, | ||
{Name: &storagePropName, Value: &storageConnection}, | ||
{Name: &functionVersionPropName, Value: &functionVersion}, | ||
{Name: &contentSharePropName, Value: &contentShare}, | ||
{Name: &contentFileConnStringPropName, Value: &storageConnection}, | ||
} | ||
} | ||
|
||
func expandFunctionAppAppSettings(d *schema.ResourceData) *map[string]*string { | ||
output := expandAppServiceAppSettings(d) | ||
|
||
basicAppSettings := getBasicFunctionAppAppSettings(d) | ||
for _, p := range basicAppSettings { | ||
(*output)[*p.Name] = p.Value | ||
} | ||
|
||
return output | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we link to the existing issue i.e.