-
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
azurerm_windows_function_app
, azurerm_linux_function_app
- adding property website_content_share_over_vnet
#26646
base: main
Are you sure you want to change the base?
Changes from 3 commits
367747e
1e92ca4
cb7988a
9b2127c
f808647
b99344c
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 |
---|---|---|
|
@@ -70,6 +70,7 @@ type LinuxFunctionAppModel struct { | |
PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` | ||
PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` | ||
Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` | ||
VnetContentShareEnabled bool `tfschema:"website_content_over_vnet"` | ||
|
||
// Computed | ||
CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` | ||
|
@@ -304,6 +305,13 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { | |
ValidateFunc: validation.StringIsNotEmpty, | ||
Description: "The local path and filename of the Zip packaged application to deploy to this Linux Function App. **Note:** Using this value requires either `WEBSITE_RUN_FROM_PACKAGE=1` or `SCM_DO_BUILD_DURING_DEPLOYMENT=true` to be set on the App in `app_settings`.", | ||
}, | ||
|
||
"website_content_over_vnet": { | ||
Type: pluginsdk.TypeBool, | ||
Optional: true, | ||
Default: false, | ||
Description: "Should the app scale enabled when storage account restricted to a virtual network? Defaults to `false`.", | ||
}, | ||
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. As above, this should be I also think the |
||
} | ||
} | ||
|
||
|
@@ -483,13 +491,14 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { | |
if !functionApp.StorageUsesMSI { | ||
suffix := uuid.New().String()[0:4] | ||
_, contentOverVnetEnabled := functionApp.AppSettings["WEBSITE_CONTENTOVERVNET"] | ||
contentOverVnetEnabledSiteSetting := functionApp.VnetContentShareEnabled | ||
_, contentSharePresent := functionApp.AppSettings["WEBSITE_CONTENTSHARE"] | ||
if _, contentShareConnectionStringPresent := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !contentShareConnectionStringPresent { | ||
functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString | ||
} | ||
|
||
if !contentSharePresent { | ||
if contentOverVnetEnabled { | ||
if contentOverVnetEnabled || contentOverVnetEnabledSiteSetting { | ||
return fmt.Errorf("the app_setting WEBSITE_CONTENTSHARE must be specified and set to a valid share when WEBSITE_CONTENTOVERVNET is specified") | ||
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. This error message needs additional clarification as there will be 2 ways this value can be true? |
||
} | ||
functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) | ||
|
@@ -515,14 +524,15 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc { | |
Kind: pointer.To("functionapp,linux"), | ||
Identity: expandedIdentity, | ||
Properties: &webapps.SiteProperties{ | ||
ServerFarmId: pointer.To(functionApp.ServicePlanId), | ||
Enabled: pointer.To(functionApp.Enabled), | ||
HTTPSOnly: pointer.To(functionApp.HttpsOnly), | ||
SiteConfig: siteConfig, | ||
ClientCertEnabled: pointer.To(functionApp.ClientCertEnabled), | ||
ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), | ||
DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), // TODO - Investigate, setting appears silently ignored on Linux Function Apps? | ||
VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, | ||
ServerFarmId: pointer.To(functionApp.ServicePlanId), | ||
Enabled: pointer.To(functionApp.Enabled), | ||
HTTPSOnly: pointer.To(functionApp.HttpsOnly), | ||
SiteConfig: siteConfig, | ||
ClientCertEnabled: pointer.To(functionApp.ClientCertEnabled), | ||
ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), | ||
DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), // TODO - Investigate, setting appears silently ignored on Linux Function Apps? | ||
VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, | ||
VnetContentShareEnabled: pointer.To(functionApp.VnetContentShareEnabled), | ||
}, | ||
} | ||
|
||
|
@@ -764,6 +774,7 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc { | |
state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) | ||
state.DefaultHostname = pointer.From(props.DefaultHostName) | ||
state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) | ||
state.VnetContentShareEnabled = pointer.From(props.VnetContentShareEnabled) | ||
|
||
servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmId) | ||
if err != nil { | ||
|
@@ -1044,6 +1055,10 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc { | |
model.Properties.SiteConfig.PublicNetworkAccess = model.Properties.PublicNetworkAccess | ||
} | ||
|
||
if metadata.ResourceData.HasChange("website_content_over_vnet") { | ||
model.Properties.VnetContentShareEnabled = pointer.To(state.VnetContentShareEnabled) | ||
} | ||
|
||
if err := client.CreateOrUpdateThenPoll(ctx, *id, model); err != nil { | ||
return fmt.Errorf("updating Linux %s: %+v", id, err) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,6 +191,42 @@ func TestAccLinuxFunctionApp_basicElasticPremiumPlan(t *testing.T) { | |
}) | ||
} | ||
|
||
func TestAccLinuxFunctionApp_websiteContentOverVnetUpdate(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") | ||
r := LinuxFunctionAppResource{} | ||
|
||
data.ResourceTest(t, r, []acceptance.TestStep{ | ||
{ | ||
Config: r.websiteContentOverVnet(data, SkuElasticPremiumPlan, "1", false), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep("app_settings.WEBSITE_CONTENTSHARE", "app_settings.%", "site_credential.0.password"), | ||
{ | ||
Config: r.websiteContentOverVnet(data, SkuElasticPremiumPlan, "1", true), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep("app_settings.WEBSITE_CONTENTSHARE", "app_settings.%", "site_credential.0.password"), | ||
{ | ||
Config: r.websiteContentOverVnet(data, SkuElasticPremiumPlan, "0", true), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep("app_settings.WEBSITE_CONTENTSHARE", "app_settings.%", "site_credential.0.password"), | ||
{ | ||
Config: r.websiteContentOverVnet(data, SkuElasticPremiumPlan, "0", false), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep("app_settings.WEBSITE_CONTENTSHARE", "app_settings.%", "site_credential.0.password"), | ||
}) | ||
} | ||
|
||
func TestAccLinuxFunctionApp_basicPremiumAppServicePlan(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azurerm_linux_function_app", "test") | ||
r := LinuxFunctionAppResource{} | ||
|
@@ -1820,6 +1856,45 @@ resource "azurerm_linux_function_app" "test" { | |
`, r.template(data, planSku), data.RandomInteger) | ||
} | ||
|
||
// nolint: unparam | ||
func (r LinuxFunctionAppResource) websiteContentOverVnet(data acceptance.TestData, planSku string, websiteContentAppSetting string, websiteContentSiteConfig bool) string { | ||
return fmt.Sprintf(` | ||
provider "azurerm" { | ||
features {} | ||
} | ||
|
||
%s | ||
|
||
resource "azurerm_storage_share" "test" { | ||
name = "shareforfa" | ||
storage_account_name = azurerm_storage_account.test.name | ||
quota = 5 | ||
} | ||
|
||
resource "azurerm_linux_function_app" "test" { | ||
name = "acctest-LFA-%d" | ||
location = azurerm_resource_group.test.location | ||
resource_group_name = azurerm_resource_group.test.name | ||
service_plan_id = azurerm_service_plan.test.id | ||
|
||
storage_account_name = azurerm_storage_account.test.name | ||
storage_account_access_key = azurerm_storage_account.test.primary_access_key | ||
|
||
app_settings = { | ||
WEBSITE_CONTENTOVERVNET = "%s" | ||
WEBSITE_CONTENTSHARE = "shareforfa" | ||
} | ||
|
||
site_config {} | ||
website_content_over_vnet = %t | ||
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. We should not be setting both of these? If we're promoting this to be a top level attribute we should be excluding it's use in the |
||
|
||
lifecycle { | ||
ignore_changes = [webdeploy_publish_basic_authentication_enabled, ftp_publish_basic_authentication_enabled] | ||
} | ||
} | ||
`, r.template(data, planSku), data.RandomInteger, websiteContentAppSetting, websiteContentSiteConfig) | ||
} | ||
|
||
func (r LinuxFunctionAppResource) withCors(data acceptance.TestData, planSku string) string { | ||
return fmt.Sprintf(` | ||
provider "azurerm" { | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -69,6 +69,7 @@ type WindowsFunctionAppModel struct { | |||||||||||||||||||||||||
ZipDeployFile string `tfschema:"zip_deploy_file"` | ||||||||||||||||||||||||||
PublishingDeployBasicAuthEnabled bool `tfschema:"webdeploy_publish_basic_authentication_enabled"` | ||||||||||||||||||||||||||
PublishingFTPBasicAuthEnabled bool `tfschema:"ftp_publish_basic_authentication_enabled"` | ||||||||||||||||||||||||||
VnetContentShareEnabled bool `tfschema:"website_content_over_vnet"` | ||||||||||||||||||||||||||
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. as above:
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Computed | ||||||||||||||||||||||||||
CustomDomainVerificationId string `tfschema:"custom_domain_verification_id"` | ||||||||||||||||||||||||||
|
@@ -303,6 +304,13 @@ func (r WindowsFunctionAppResource) Arguments() map[string]*pluginsdk.Schema { | |||||||||||||||||||||||||
ValidateFunc: validation.StringIsNotEmpty, | ||||||||||||||||||||||||||
Description: "The local path and filename of the Zip packaged application to deploy to this Windows Function App. **Note:** Using this value requires `WEBSITE_RUN_FROM_PACKAGE=1` to be set on the App in `app_settings`.", | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
"website_content_over_vnet": { | ||||||||||||||||||||||||||
Type: pluginsdk.TypeBool, | ||||||||||||||||||||||||||
Optional: true, | ||||||||||||||||||||||||||
Default: false, | ||||||||||||||||||||||||||
Description: "Should the app scale enabled when storage account restricted to a virtual network? Defaults to `false`.", | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
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. as above:
Suggested change
Similarly, can you review the description here, it doesn't make sense to me? |
||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -485,13 +493,14 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { | |||||||||||||||||||||||||
if !functionApp.StorageUsesMSI { | ||||||||||||||||||||||||||
suffix := uuid.New().String()[0:4] | ||||||||||||||||||||||||||
_, contentOverVnetEnabled := functionApp.AppSettings["WEBSITE_CONTENTOVERVNET"] | ||||||||||||||||||||||||||
contentOverVnetEnabledSiteSetting := functionApp.VnetContentShareEnabled | ||||||||||||||||||||||||||
_, contentSharePresent := functionApp.AppSettings["WEBSITE_CONTENTSHARE"] | ||||||||||||||||||||||||||
if _, contentShareConnectionStringPresent := functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !contentShareConnectionStringPresent { | ||||||||||||||||||||||||||
functionApp.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if !contentSharePresent { | ||||||||||||||||||||||||||
if contentOverVnetEnabled { | ||||||||||||||||||||||||||
if contentOverVnetEnabled || contentOverVnetEnabledSiteSetting { | ||||||||||||||||||||||||||
return fmt.Errorf("the app_setting WEBSITE_CONTENTSHARE must be specified and set to a valid share when WEBSITE_CONTENTOVERVNET is specified") | ||||||||||||||||||||||||||
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. As above, this needs to be clear about the property, not just the |
||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
functionApp.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(functionApp.Name), suffix) | ||||||||||||||||||||||||||
|
@@ -516,14 +525,15 @@ func (r WindowsFunctionAppResource) Create() sdk.ResourceFunc { | |||||||||||||||||||||||||
Kind: pointer.To("functionapp"), | ||||||||||||||||||||||||||
Identity: expandedIdentity, | ||||||||||||||||||||||||||
Properties: &webapps.SiteProperties{ | ||||||||||||||||||||||||||
ServerFarmId: pointer.To(functionApp.ServicePlanId), | ||||||||||||||||||||||||||
Enabled: pointer.To(functionApp.Enabled), | ||||||||||||||||||||||||||
HTTPSOnly: pointer.To(functionApp.HttpsOnly), | ||||||||||||||||||||||||||
SiteConfig: siteConfig, | ||||||||||||||||||||||||||
ClientCertEnabled: pointer.To(functionApp.ClientCertEnabled), | ||||||||||||||||||||||||||
ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), | ||||||||||||||||||||||||||
DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), | ||||||||||||||||||||||||||
VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, | ||||||||||||||||||||||||||
ServerFarmId: pointer.To(functionApp.ServicePlanId), | ||||||||||||||||||||||||||
Enabled: pointer.To(functionApp.Enabled), | ||||||||||||||||||||||||||
HTTPSOnly: pointer.To(functionApp.HttpsOnly), | ||||||||||||||||||||||||||
SiteConfig: siteConfig, | ||||||||||||||||||||||||||
ClientCertEnabled: pointer.To(functionApp.ClientCertEnabled), | ||||||||||||||||||||||||||
ClientCertMode: pointer.To(webapps.ClientCertMode(functionApp.ClientCertMode)), | ||||||||||||||||||||||||||
DailyMemoryTimeQuota: pointer.To(functionApp.DailyMemoryTimeQuota), | ||||||||||||||||||||||||||
VnetRouteAllEnabled: siteConfig.VnetRouteAllEnabled, | ||||||||||||||||||||||||||
VnetContentShareEnabled: pointer.To(functionApp.VnetContentShareEnabled), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -751,6 +761,7 @@ func (r WindowsFunctionAppResource) Read() sdk.ResourceFunc { | |||||||||||||||||||||||||
state.CustomDomainVerificationId = pointer.From(props.CustomDomainVerificationId) | ||||||||||||||||||||||||||
state.DefaultHostname = pointer.From(props.DefaultHostName) | ||||||||||||||||||||||||||
state.PublicNetworkAccess = !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled) | ||||||||||||||||||||||||||
state.VnetContentShareEnabled = pointer.From(props.VnetContentShareEnabled) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(pointer.From(props.ServerFarmId)) | ||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||
|
@@ -1007,13 +1018,14 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { | |||||||||||||||||||||||||
if !state.StorageUsesMSI { | ||||||||||||||||||||||||||
suffix := uuid.New().String()[0:4] | ||||||||||||||||||||||||||
_, contentOverVnetEnabled := state.AppSettings["WEBSITE_CONTENTOVERVNET"] | ||||||||||||||||||||||||||
contentOverVnetEnabledSiteSetting := state.VnetContentShareEnabled | ||||||||||||||||||||||||||
_, contentSharePresent := state.AppSettings["WEBSITE_CONTENTSHARE"] | ||||||||||||||||||||||||||
if _, contentShareConnectionStringPresent := state.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]; !contentShareConnectionStringPresent { | ||||||||||||||||||||||||||
state.AppSettings["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"] = storageString | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if !contentSharePresent { | ||||||||||||||||||||||||||
if contentOverVnetEnabled { | ||||||||||||||||||||||||||
if contentOverVnetEnabled || contentOverVnetEnabledSiteSetting { | ||||||||||||||||||||||||||
return fmt.Errorf("the value of WEBSITE_CONTENTSHARE must be set to a predefined share when the storage account is restricted to a virtual network") | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
state.AppSettings["WEBSITE_CONTENTSHARE"] = fmt.Sprintf("%s-%s", strings.ToLower(state.Name), suffix) | ||||||||||||||||||||||||||
|
@@ -1060,6 +1072,10 @@ func (r WindowsFunctionAppResource) Update() sdk.ResourceFunc { | |||||||||||||||||||||||||
model.Properties.SiteConfig.PublicNetworkAccess = model.Properties.PublicNetworkAccess | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if metadata.ResourceData.HasChange("website_content_over_vnet") { | ||||||||||||||||||||||||||
model.Properties.VnetContentShareEnabled = pointer.To(state.VnetContentShareEnabled) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if err := client.CreateOrUpdateThenPoll(ctx, *id, model); err != nil { | ||||||||||||||||||||||||||
return fmt.Errorf("updating Windows %s: %+v", id, err) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,6 +115,10 @@ In addition to the Arguments listed above - the following Attributes are exporte | |
|
||
* `webdeploy_publish_basic_authentication_enabled` - Are the default WebDeploy Basic Authentication publishing credentials enabled. | ||
|
||
* `website_content_over_vnet` - Is function app scale enabled when the linked storage account is restricted to a virtual network? Defaults to `false`. | ||
|
||
!> **Note:** This property will replace the `WEBSITE_CONTENTOVERVNET` in `app_setting`. `website_content_over_vnet` is the recommended one despite both settings work currently. | ||
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. This description doesn't sound like it matches what the setting does, it doesn't relate to app scale, rather if the storage account is accessed via VNet? |
||
|
||
--- | ||
|
||
An `active_directory` block exports the following: | ||
|
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.
Following contributor guidelines, this should be: