DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

Bicep: Deploy Function App with VNET integration

Virtual Network Integration feature is important to secure ingress/egress of Function App. See Azure Functions networking options for more detail.

In this article, I explain how we can deploy such configuration via bicep.

Basic bicep

I use Quickstart: Create and deploy Azure Functions resources using Bicep as base script.

Add VNET section

Firstly, we need VNET to use VNET integration. So I added following code before creating storage account.

  • We need to set delegation to support VNET integration. Set it for Microsoft.Web/serverFarms. See What is subnet delegation? for detail.
var vnetName = 'vnet-${appName}' var subnetNeme = 'snet-${appName}' resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' = { name: vnetName location: location properties: { addressSpace: { addressPrefixes: [ '10.0.0.0/16' ] } subnets: [ { name: subnetNeme properties: { addressPrefix: '10.0.0.0/24' delegations: [ { name: 'Microsoft.Web/serverFarms' properties: { serviceName: 'Microsoft.Web/serverFarms' } } ] } } ] } } 
Enter fullscreen mode Exit fullscreen mode

Change SKU

Serverless plan doesn't support VNET Integration, so I changed SKU to Standard for hosting plan.

resource hostingPlan 'Microsoft.Web/serverfarms@2021-03-01' = { name: hostingPlanName location: location sku: { name: 'S1' tier: 'Standard' } properties: {} } 
Enter fullscreen mode Exit fullscreen mode

Add VNET info

Finally, I added virtualNetworkSubnetId property for functionApp.

resource functionApp 'Microsoft.Web/sites@2021-03-01' = { name: functionAppName location: location kind: 'functionapp' identity: { type: 'SystemAssigned' } properties: { virtualNetworkSubnetId: vnet.properties.subnets[0].id serverFarmId: hostingPlan.id 
Enter fullscreen mode Exit fullscreen mode

Complete Bicep

@description('The name of the function app that you wish to create.') param appName string = 'fnapp${uniqueString(resourceGroup().id)}' @description('Storage Account type') @allowed([ 'Standard_LRS' 'Standard_GRS' 'Standard_RAGRS' ]) param storageAccountType string = 'Standard_LRS' @description('Location for all resources.') param location string = resourceGroup().location @description('Location for Application Insights') param appInsightsLocation string @description('The language worker runtime to load in the function app.') @allowed([ 'node' 'dotnet' 'java' ]) param runtime string = 'node' var functionAppName = appName var hostingPlanName = appName var applicationInsightsName = appName var vnetName = 'vnet-${appName}' var subnetNeme = 'snet-${appName}' var storageAccountName = '${uniqueString(resourceGroup().id)}azfunctions' var functionWorkerRuntime = runtime resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' = { name: vnetName location: location properties: { addressSpace: { addressPrefixes: [ '10.0.0.0/16' ] } subnets: [ { name: subnetNeme properties: { addressPrefix: '10.0.0.0/24' delegations: [ { name: 'Microsoft.Web/serverFarms' properties: { serviceName: 'Microsoft.Web/serverFarms' } } ] } } ] } } resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { name: storageAccountName location: location sku: { name: storageAccountType } kind: 'Storage' } resource hostingPlan 'Microsoft.Web/serverfarms@2021-03-01' = { name: hostingPlanName location: location sku: { name: 'S1' tier: 'Standard' } properties: {} } resource functionApp 'Microsoft.Web/sites@2021-03-01' = { name: functionAppName location: location kind: 'functionapp' identity: { type: 'SystemAssigned' } properties: { virtualNetworkSubnetId: vnet.properties.subnets[0].id serverFarmId: hostingPlan.id siteConfig: { appSettings: [ { name: 'AzureWebJobsStorage' value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}' } { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}' } { name: 'WEBSITE_CONTENTSHARE' value: toLower(functionAppName) } { name: 'FUNCTIONS_EXTENSION_VERSION' value: '~2' } { name: 'WEBSITE_NODE_DEFAULT_VERSION' value: '~10' } { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' value: applicationInsights.properties.InstrumentationKey } { name: 'FUNCTIONS_WORKER_RUNTIME' value: functionWorkerRuntime } ] ftpsState: 'FtpsOnly' minTlsVersion: '1.2' } httpsOnly: true } } resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { name: applicationInsightsName location: appInsightsLocation kind: 'web' properties: { Application_Type: 'web' Request_Source: 'rest' } } 
Enter fullscreen mode Exit fullscreen mode

Result

Image description

Hope this helps me in the future!

Top comments (1)

Collapse
 
cricam profile image
cricam

thanks!