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 (0)