diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/README.md b/quickstarts/microsoft.web/function-app-storage-private-endpoints/README.md index e13829d7f2dc..41862b82c0f9 100644 --- a/quickstarts/microsoft.web/function-app-storage-private-endpoints/README.md +++ b/quickstarts/microsoft.web/function-app-storage-private-endpoints/README.md @@ -9,11 +9,13 @@ ![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.web/function-app-storage-private-endpoints/BestPracticeResult.svg) ![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.web/function-app-storage-private-endpoints/CredScanResult.svg) +![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.web/function-app-storage-private-endpoints/BicepVersion.svg) + [![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fquickstarts%2Fmicrosoft.web%2Ffunction-app-storage-private-endpoints%2Fazuredeploy.json) [![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fquickstarts%2Fmicrosoft.web%2Ffunction-app-storage-private-endpoints%2Fazuredeploy.json) This sample Azure Resource Manager template deploys an Azure Function App that communicates with the Azure Storage account referenced by the [AzureWebJobsStorage](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#azurewebjobsstorage) and [WEBSITE_CONTENTAZUREFILECONNECTIONSTRING](https://docs.microsoft.com/azure/azure-functions/functions-app-settings#website_contentazurefileconnectionstring) app settings, [via private endpoints](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#private-endpoints). -![Function App with Storage Private Endpoints](/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-privateendponts.png) +![Function App with Storage Private Endpoints](/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg) ### Azure Function App diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/azuredeploy.json b/quickstarts/microsoft.web/function-app-storage-private-endpoints/azuredeploy.json index 25ad1ee21a08..52cf3ec4198d 100644 --- a/quickstarts/microsoft.web/function-app-storage-private-endpoints/azuredeploy.json +++ b/quickstarts/microsoft.web/function-app-storage-private-endpoints/azuredeploy.json @@ -1,483 +1,459 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.4.451.19169", + "templateHash": "8462918231759969902" + } + }, "parameters": { - "Location": { + "location": { "type": "string", "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Location to deploy resources to." + "description": "The location into which the resources should be deployed." } }, - "AppName": { + "functionAppName": { "type": "string", - "defaultValue": "[concat('FuncApp-', uniqueString(resourceGroup().id))]", + "defaultValue": "[format('func-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Name of the Function App." + "description": "The name of the Azure Function app." } }, - "isReserved": { - "type": "bool", - "defaultValue": false, + "functionAppPlanName": { + "type": "string", + "defaultValue": "[format('plan-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Set to true if Linux." + "description": "The name of the Azure Function hosting plan." } }, - "plan": { + "functionPlanOS": { "type": "string", - "defaultValue": "[concat(parameters('AppName'), '-asp')]", + "defaultValue": "Windows", + "allowedValues": [ + "Windows", + "Linux" + ], "metadata": { - "description": "Name of the Elastic Premium Plan for the Function App." + "description": "Specifies the OS used for the Azure Function hosting plan." } }, - "skuName": { + "functionAppPlanSku": { "type": "string", "defaultValue": "EP1", + "allowedValues": [ + "EP1", + "EP2", + "EP3" + ], "metadata": { - "description": "The Sku name/size of the Elastic Premium plan" + "description": "Specifies the Azure Function hosting plan SKU." } }, - "vnetName": { + "functionStorageAccountName": { "type": "string", - "defaultValue": "VirtualNetwork", + "defaultValue": "[format('st{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "Name of the VNET that the Function App and Storage account will communicate over." + "description": "The name of the backend Azure storage account used by the Azure Function app." } }, - "functionsSubnetName": { + "vnetName": { "type": "string", - "defaultValue": "FunctionsSubnet", + "defaultValue": "[format('vnet-{0}', uniqueString(resourceGroup().id))]", "metadata": { - "description": "The subnet that the Function App will use for VNET traffic." + "description": "The name of the virtual network for virtual network integration." } }, - "storagePESubnetName": { + "functionSubnetName": { "type": "string", - "defaultValue": "StoragePESubnet", + "defaultValue": "snet-func", "metadata": { - "description": "The subnet that will be used for the Storage private endpoints." + "description": "The name of the virtual network subnet to be associated with the Azure Function app." } }, - "StorageAccountName": { + "privateEndpointSubnetName": { "type": "string", - "defaultValue": "[toLower(concat(uniqueString(resourceGroup().id), 'pe'))]", + "defaultValue": "snet-pe", "metadata": { - "description": "Name of the Storage account that the Function App will use for operations and content." + "description": "The name of the virtual network subnet used for allocating IP addresses for private endpoints." } }, - "virtualNetworkAddressPrefix": { + "vnetAddressPrefix": { "type": "string", "defaultValue": "10.100.0.0/16", "metadata": { - "description": "VNET address space." + "description": "The IP adddress space used for the virtual network." } }, "functionSubnetAddressPrefix": { "type": "string", "defaultValue": "10.100.0.0/24", "metadata": { - "description": "Function App's subnet address range." + "description": "The IP address space used for the Azure Function integration subnet." } }, "privateEndpointSubnetAddressPrefix": { "type": "string", "defaultValue": "10.100.1.0/24", "metadata": { - "description": "Storage account's private endpoint's subnet address range." + "description": "The IP address space used for the private endpoints." } } }, + "functions": [], "variables": { - "fileShareName": "[concat(toLower(parameters('AppName')), 'b86e')]", - "privateStorageFileDnsZoneName": "[concat('privatelink.file.', environment().suffixes.storage)]", - "privateStorageBlobDnsZoneName": "[concat('privatelink.blob.', environment().suffixes.storage)]", - "privateStorageQueueDnsZoneName": "[concat('privatelink.queue.', environment().suffixes.storage)]", - "privateStorageTableDnsZoneName": "[concat('privatelink.table.', environment().suffixes.storage)]", - "privateEndpointFileStorageName": "[concat(parameters('StorageAccountName'), '-file-private-endpoint')]", - "privateEndpointBlobStorageName": "[concat(parameters('StorageAccountName'), '-blob-private-endpoint')]", - "privateEndpointQueueStorageName": "[concat(parameters('StorageAccountName'), '-queue-private-endpoint')]", - "privateEndpointTableStorageName": "[concat(parameters('StorageAccountName'), '-table-private-endpoint')]", - "virtualNetworkLinksSuffixFileStorageName": "[concat(variables('privateStorageFileDnsZoneName'), '-link')]", - "virtualNetworkLinksSuffixBlobStorageName": "[concat(variables('privateStorageBlobDnsZoneName'), '-link')]", - "virtualNetworkLinksSuffixQueueStorageName": "[concat(variables('privateStorageQueueDnsZoneName'), '-link')]", - "virtualNetworkLinksSuffixTableStorageName": "[concat(variables('privateStorageTableDNSZoneName'), '-link')]" + "applicationInsightsName": "[format('appi-{0}', uniqueString(resourceGroup().id))]", + "privateStorageFileDnsZoneName": "[format('privatelink.file.{0}', environment().suffixes.storage)]", + "privateEndpointStorageFileName": "[format('{0}-file-private-endpoint', parameters('functionStorageAccountName'))]", + "privateStorageTableDnsZoneName": "[format('privatelink.table.{0}', environment().suffixes.storage)]", + "privateEndpointStorageTableName": "[format('{0}-table-private-endpoint', parameters('functionStorageAccountName'))]", + "privateStorageBlobDnsZoneName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]", + "privateEndpointStorageBlobName": "[format('{0}-blob-private-endpoint', parameters('functionStorageAccountName'))]", + "privateStorageQueueDnsZoneName": "[format('privatelink.queue.{0}', environment().suffixes.storage)]", + "privateEndpointStorageQueueName": "[format('{0}-queue-private-endpoint', parameters('functionStorageAccountName'))]", + "functionContentShareName": "function-content-share", + "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]" }, "resources": [ { "type": "Microsoft.Network/virtualNetworks", "apiVersion": "2020-07-01", - "location": "[parameters('Location')]", "name": "[parameters('vnetName')]", + "location": "[parameters('location')]", "properties": { "addressSpace": { "addressPrefixes": [ - "[parameters('virtualNetworkAddressPrefix')]" + "[parameters('vnetAddressPrefix')]" ] }, "subnets": [ { - "name": "[parameters('functionsSubnetName')]", + "name": "[parameters('functionSubnetName')]", "properties": { - "addressPrefix": "[parameters('functionSubnetAddressPrefix')]", "privateEndpointNetworkPolicies": "Enabled", "privateLinkServiceNetworkPolicies": "Enabled", "delegations": [ { "name": "webapp", "properties": { - "serviceName": "Microsoft.Web/serverFarms", - "actions": [ - "Microsoft.Network/virtualNetworks/subnets/action" - ] + "serviceName": "Microsoft.Web/serverFarms" } } - ] + ], + "addressPrefix": "[parameters('functionSubnetAddressPrefix')]" } }, { - "name": "[parameters('storagePESubnetName')]", + "name": "[parameters('privateEndpointSubnetName')]", "properties": { - "addressPrefix": "[parameters('privateEndpointSubnetAddressPrefix')]", + "privateEndpointNetworkPolicies": "Disabled", "privateLinkServiceNetworkPolicies": "Enabled", - "privateEndpointNetworkPolicies": "Disabled" + "addressPrefix": "[parameters('privateEndpointSubnetAddressPrefix')]" } } - ], - "enableDdosProtection": false, - "enableVmProtection": false - } - }, - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2021-04-01", - "location": "[parameters('Location')]", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], - "name": "[parameters('StorageAccountName')]", - "sku": { - "name": "Standard_LRS", - "tier": "Standard" - }, - "kind": "StorageV2", - "properties": { - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Deny" - }, - "supportsHttpsTrafficOnly": true, - "encryption": { - "services": { - "file": { - "keyType": "Account", - "enabled": true - }, - "blob": { - "keyType": "Account", - "enabled": true - } - }, - "keySource": "Microsoft.Storage" - } + ] } }, - { - "type": "Microsoft.Storage/storageAccounts/fileServices/shares", - "apiVersion": "2021-04-01", - "name": "[concat(parameters('StorageAccountName'), '/default/', variables('fileShareName'))]", - "dependsOn": [ - "[parameters('StorageAccountName')]" - ] - }, { "type": "Microsoft.Network/privateDnsZones", "apiVersion": "2020-06-01", "name": "[variables('privateStorageFileDnsZoneName')]", - "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ] + "location": "global" }, { "type": "Microsoft.Network/privateDnsZones", "apiVersion": "2020-06-01", "name": "[variables('privateStorageBlobDnsZoneName')]", - "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ] + "location": "global" }, { "type": "Microsoft.Network/privateDnsZones", "apiVersion": "2020-06-01", "name": "[variables('privateStorageQueueDnsZoneName')]", - "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ] + "location": "global" }, { "type": "Microsoft.Network/privateDnsZones", "apiVersion": "2020-06-01", "name": "[variables('privateStorageTableDnsZoneName')]", - "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ] + "location": "global" }, { "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", "apiVersion": "2020-06-01", - "name": "[concat(variables('privateStorageFileDnsZoneName'), '/', variables('virtualNetworkLinksSuffixFileStorageName'))]", + "name": "[format('{0}/{1}', variables('privateStorageFileDnsZoneName'), format('{0}-link', variables('privateStorageFileDnsZoneName')))]", "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones',variables('privateStorageFileDnsZoneName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], "properties": { "registrationEnabled": false, "virtualNetwork": { "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", "apiVersion": "2020-06-01", - "name": "[concat(variables('privateStorageBlobDnsZoneName'), '/', variables('virtualNetworkLinksSuffixBlobStorageName'))]", + "name": "[format('{0}/{1}', variables('privateStorageBlobDnsZoneName'), format('{0}-link', variables('privateStorageBlobDnsZoneName')))]", "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], "properties": { "registrationEnabled": false, "virtualNetwork": { "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", "apiVersion": "2020-06-01", - "name": "[concat(variables('privateStorageQueueDnsZoneName'), '/', variables('virtualNetworkLinksSuffixQueueStorageName'))]", + "name": "[format('{0}/{1}', variables('privateStorageTableDnsZoneName'), format('{0}-link', variables('privateStorageTableDnsZoneName')))]", "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], "properties": { "registrationEnabled": false, "virtualNetwork": { "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", "apiVersion": "2020-06-01", - "name": "[concat(variables('privateStorageTableDnsZoneName'), '/', variables('virtualNetworkLinksSuffixTableStorageName'))]", + "name": "[format('{0}/{1}', variables('privateStorageQueueDnsZoneName'), format('{0}-link', variables('privateStorageQueueDnsZoneName')))]", "location": "global", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones',variables('privateStorageTableDnsZoneName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], "properties": { "registrationEnabled": false, "virtualNetwork": { "id": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { - "type": "Microsoft.Network/privateEndpoints", - "name": "[variables('privateEndpointFileStorageName')]", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('privateEndpointStorageFileName'), 'filePrivateDnsZoneGroup')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "config", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]" + } + } + ] + }, "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('StorageAccountName'), 'default', variables('fileShareName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageFileName'))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('privateEndpointStorageBlobName'), 'blobPrivateDnsZoneGroup')]", "properties": { - "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('storagePESubnetName') )]" - }, - "privateLinkServiceConnections": [ + "privateDnsZoneConfigs": [ { - "name": "MyStorageQueuePrivateLinkConnection", + "name": "config", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName'))]", - "groupIds": [ - "file" - ] + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]" } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageBlobName'))]" + ] }, { - "type": "Microsoft.Network/privateEndpoints", - "name": "[variables('privateEndpointBlobStorageName')]", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('privateEndpointStorageTableName'), 'tablePrivateDnsZoneGroup')]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "config", + "properties": { + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]" + } + } + ] + }, "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('StorageAccountName'), 'default', variables('fileShareName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageTableName'))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('privateEndpointStorageQueueName'), 'tablePrivateDnsZoneGroup')]", "properties": { - "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('storagePESubnetName') )]" - }, - "privateLinkServiceConnections": [ + "privateDnsZoneConfigs": [ { - "name": "MyStorageQueuePrivateLinkConnection", + "name": "config", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName'))]", - "groupIds": [ - "blob" - ] + "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]" } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointStorageQueueName'))]" + ] }, { "type": "Microsoft.Network/privateEndpoints", - "name": "[variables('privateEndpointQueueStorageName')]", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('StorageAccountName'), 'default', variables('fileShareName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointStorageFileName')]", + "location": "[parameters('location')]", "properties": { "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('storagePESubnetName'))]" + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]" }, "privateLinkServiceConnections": [ { - "name": "MyStorageQueuePrivateLinkConnection", + "name": "MyStorageFilePrivateLinkConnection", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName'))]", + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", "groupIds": [ - "queue" + "file" ] } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { "type": "Microsoft.Network/privateEndpoints", - "name": "[variables('privateEndpointTableStorageName')]", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('StorageAccountName'), 'default', variables('fileShareName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointStorageTableName')]", + "location": "[parameters('location')]", "properties": { "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('storagePESubnetName'))]" + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]" }, "privateLinkServiceConnections": [ { - "name": "MyStorageQueuePrivateLinkConnection", + "name": "MyStorageTablePrivateLinkConnection", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName'))]", + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", "groupIds": [ "table" ] } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "name": "[concat(variables('privateEndpointFileStorageName'), '/default')]", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointFileStorageName'))]" - ], + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointStorageQueueName')]", + "location": "[parameters('location')]", "properties": { - "privateDnsZoneConfigs": [ + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]" + }, + "privateLinkServiceConnections": [ { - "name": "config1", + "name": "MyStorageQueuePrivateLinkConnection", "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageFileDnsZoneName'))]" + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "groupIds": [ + "queue" + ] } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "name": "[concat(variables('privateEndpointBlobStorageName'), '/default')]", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointBlobStorageName'))]" - ], + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2021-02-01", + "name": "[variables('privateEndpointStorageBlobName')]", + "location": "[parameters('location')]", "properties": { - "privateDnsZoneConfigs": [ + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('privateEndpointSubnetName'))]" + }, + "privateLinkServiceConnections": [ { - "name": "config1", + "name": "MyStorageBlobPrivateLinkConnection", "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageBlobDnsZoneName'))]" + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "groupIds": [ + "blob" + ] } } ] - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "name": "[concat(variables('privateEndpointQueueStorageName'), '/default')]", - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointQueueStorageName'))]" - ], + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-02-01", + "name": "[parameters('functionStorageAccountName')]", + "location": "[parameters('location')]", + "kind": "StorageV2", + "sku": { + "name": "Standard_LRS" + }, "properties": { - "privateDnsZoneConfigs": [ - { - "name": "config1", - "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageQueueDnsZoneName'))]" - } - } - ] + "networkAcls": { + "bypass": "None", + "defaultAction": "Deny" + } } }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2020-06-01", - "location": "[parameters('Location')]", - "name": "[concat(variables('privateEndpointTableStorageName'), '/default')]", + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2021-04-01", + "name": "[format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName'))]", "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpointTableStorageName'))]" - ], - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "config1", - "properties": { - "privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateStorageTableDnsZoneName'))]" - } - } - ] - } + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]" + ] }, { "type": "Microsoft.Insights/components", "apiVersion": "2020-02-02", - "location": "[parameters('Location')]", - "name": "[parameters('AppName')]", + "name": "[variables('applicationInsightsName')]", + "location": "[parameters('location')]", "kind": "web", "properties": { "Application_Type": "web" @@ -485,60 +461,45 @@ }, { "type": "Microsoft.Web/serverfarms", - "apiVersion": "2020-09-01", - "name": "[parameters('plan')]", - "location": "[parameters('Location')]", + "apiVersion": "2021-01-01", + "name": "[parameters('functionAppPlanName')]", + "location": "[parameters('location')]", "sku": { - "name": "[parameters('skuName')]", + "name": "[parameters('functionAppPlanSku')]", "tier": "ElasticPremium", - "size": "[parameters('skuName')]", - "family": "EP", - "capacity": 1 + "size": "[parameters('functionAppPlanSku')]", + "family": "EP" }, "kind": "elastic", "properties": { "maximumElasticWorkerCount": 20, - "reserved": "[parameters('isReserved')]" + "reserved": "[variables('isReserved')]" } }, { "type": "Microsoft.Web/sites", - "apiVersion": "2020-09-01", - "name": "[parameters('AppName')]", - "location": "[parameters('Location')]", - "dependsOn": [ - "[resourceId('Microsoft.Web/serverfarms', parameters('plan'))]", - "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('StorageAccountName'), 'default', variables('fileShareName'))]", - "[resourceId('Microsoft.Insights/components', parameters('AppName'))]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointFileStorageName'), 'default')]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointBlobStorageName'), 'default')]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointQueueStorageName'), 'default')]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointTableStorageName'), 'default')]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageFileDnsZoneName'), variables('virtualNetworkLinksSuffixFileStorageName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageBlobDnsZoneName'), variables('virtualNetworkLinksSuffixBlobStorageName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageQueueDnsZoneName'), variables('virtualNetworkLinksSuffixQueueStorageName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageTableDnsZoneName'), variables('virtualNetworkLinksSuffixTableStorageName'))]" - ], - "kind": "[if(parameters('isReserved'), 'functionapp,linux', 'functionapp')]", + "apiVersion": "2021-01-01", + "name": "[parameters('functionAppName')]", + "location": "[parameters('location')]", + "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]", "properties": { - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('plan'))]", + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('functionAppPlanName'))]", + "reserved": "[variables('isReserved')]", "siteConfig": { + "functionsRuntimeScaleMonitoringEnabled": true, + "linuxFxVersion": "[if(variables('isReserved'), 'dotnet|3.1', json('null'))]", "appSettings": [ { "name": "APPINSIGHTS_INSTRUMENTATIONKEY", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('AppName')), '2020-02-02').instrumentationKey]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[concat('InstrumentationKey=', reference(resourceId('Microsoft.Insights/components', parameters('AppName')), '2020-02-02').instrumentationKey)]" + "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]" }, { "name": "AzureWebJobsStorage", - "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('StorageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName')), '2021-04-01').keys[0].value,';')]" + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}', parameters('functionStorageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName')), '2021-02-01').keys[0].value)]" }, { "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", - "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('StorageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', parameters('StorageAccountName')), '2021-04-01').keys[0].value,';')]" + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}', parameters('functionStorageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName')), '2021-02-01').keys[0].value)]" }, { "name": "FUNCTIONS_EXTENSION_VERSION", @@ -548,10 +509,6 @@ "name": "FUNCTIONS_WORKER_RUNTIME", "value": "dotnet" }, - { - "name": "WEBSITE_CONTENTSHARE", - "value": "[variables('fileShareName')]" - }, { "name": "WEBSITE_VNET_ROUTE_ALL", "value": "1" @@ -561,27 +518,38 @@ "value": "1" }, { - "name": "WEBSITE_DNS_SERVER", - "value": "168.63.129.16" + "name": "WEBSITE_CONTENTSHARE", + "value": "[variables('functionContentShareName')]" } - ], - "functionsRuntimeScaleMonitoringEnabled": true, - "linuxFxVersion": "[if(parameters('isReserved'), 'dotnet|3.1', json('null'))]" - }, - "reserved": "[parameters('isReserved')]" - } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]", + "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[0], split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[1], split(format('{0}/default/{1}', parameters('functionStorageAccountName'), variables('functionContentShareName')), '/')[2])]", + "[resourceId('Microsoft.Web/serverfarms', parameters('functionAppPlanName'))]", + "[resourceId('Microsoft.Storage/storageAccounts', parameters('functionStorageAccountName'))]", + "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageBlobDnsZoneName'), format('{0}-link', variables('privateStorageBlobDnsZoneName')))]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageBlobName'), 'blobPrivateDnsZoneGroup')]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageFileName'), 'filePrivateDnsZoneGroup')]", + "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageQueueDnsZoneName'), format('{0}-link', variables('privateStorageQueueDnsZoneName')))]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageQueueName'), 'tablePrivateDnsZoneGroup')]", + "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('privateStorageTableDnsZoneName'), format('{0}-link', variables('privateStorageTableDnsZoneName')))]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', variables('privateEndpointStorageTableName'), 'tablePrivateDnsZoneGroup')]" + ] }, { "type": "Microsoft.Web/sites/networkConfig", - "apiVersion": "2020-09-01", - "name": "[concat(parameters('AppName'), '/virtualNetwork')]", - "dependsOn": [ - "[resourceId('Microsoft.Web/sites', parameters('AppName'))]" - ], + "apiVersion": "2021-01-01", + "name": "[format('{0}/{1}', parameters('functionAppName'), 'virtualNetwork')]", "properties": { - "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('functionsSubnetName'))]", - "isSwift": true - } + "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('functionSubnetName'))]", + "swiftSupported": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] } ] } \ No newline at end of file diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg b/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg new file mode 100644 index 000000000000..c922eceab8ed Binary files /dev/null and b/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-private-endpoints.jpg differ diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-privateendponts.png b/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-privateendponts.png deleted file mode 100644 index 598430bc8436..000000000000 Binary files a/quickstarts/microsoft.web/function-app-storage-private-endpoints/images/function-app-storage-privateendponts.png and /dev/null differ diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/main.bicep b/quickstarts/microsoft.web/function-app-storage-private-endpoints/main.bicep new file mode 100644 index 000000000000..ca4bdbeba17f --- /dev/null +++ b/quickstarts/microsoft.web/function-app-storage-private-endpoints/main.bicep @@ -0,0 +1,433 @@ +@description('The location into which the resources should be deployed.') +param location string = resourceGroup().location + +@description('The name of the Azure Function app.') +param functionAppName string = 'func-${uniqueString(resourceGroup().id)}' + +@description('The name of the Azure Function hosting plan.') +param functionAppPlanName string = 'plan-${uniqueString(resourceGroup().id)}' + +@description('Specifies the OS used for the Azure Function hosting plan.') +@allowed([ + 'Windows' + 'Linux' +]) +param functionPlanOS string = 'Windows' + +@description('Specifies the Azure Function hosting plan SKU.') +@allowed([ + 'EP1' + 'EP2' + 'EP3' +]) +param functionAppPlanSku string = 'EP1' + +@description('The name of the backend Azure storage account used by the Azure Function app.') +param functionStorageAccountName string = 'st${uniqueString(resourceGroup().id)}' + +@description('The name of the virtual network for virtual network integration.') +param vnetName string = 'vnet-${uniqueString(resourceGroup().id)}' + +@description('The name of the virtual network subnet to be associated with the Azure Function app.') +param functionSubnetName string = 'snet-func' + +@description('The name of the virtual network subnet used for allocating IP addresses for private endpoints.') +param privateEndpointSubnetName string = 'snet-pe' + +@description('The IP adddress space used for the virtual network.') +param vnetAddressPrefix string = '10.100.0.0/16' + +@description('The IP address space used for the Azure Function integration subnet.') +param functionSubnetAddressPrefix string = '10.100.0.0/24' + +@description('The IP address space used for the private endpoints.') +param privateEndpointSubnetAddressPrefix string = '10.100.1.0/24' + +var applicationInsightsName = 'appi-${uniqueString(resourceGroup().id)}' + +var privateStorageFileDnsZoneName = 'privatelink.file.${environment().suffixes.storage}' +var privateEndpointStorageFileName = '${storageAccount.name}-file-private-endpoint' + +var privateStorageTableDnsZoneName = 'privatelink.table.${environment().suffixes.storage}' +var privateEndpointStorageTableName = '${storageAccount.name}-table-private-endpoint' + +var privateStorageBlobDnsZoneName = 'privatelink.blob.${environment().suffixes.storage}' +var privateEndpointStorageBlobName = '${storageAccount.name}-blob-private-endpoint' + +var privateStorageQueueDnsZoneName = 'privatelink.queue.${environment().suffixes.storage}' +var privateEndpointStorageQueueName = '${storageAccount.name}-queue-private-endpoint' + +var functionContentShareName = 'function-content-share' + +// The term "reserved" is used by ARM to indicate if the hosting plan is a Linux or Windows-based plan. +// A value of true indicated Linux, while a value of false indicates Windows. +// See https://docs.microsoft.com/en-us/azure/templates/microsoft.web/serverfarms?tabs=json#appserviceplanproperties-object. +var isReserved = (functionPlanOS == 'Linux') ? true : false + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-07-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + vnetAddressPrefix + ] + } + subnets: [ + { + name: functionSubnetName + properties: { + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + delegations: [ + { + name: 'webapp' + properties: { + serviceName: 'Microsoft.Web/serverFarms' + } + } + ] + addressPrefix: functionSubnetAddressPrefix + } + } + { + name: privateEndpointSubnetName + properties: { + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + addressPrefix: privateEndpointSubnetAddressPrefix + } + } + ] + } +} + +// -- Private DNS Zones -- +resource storageFileDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: privateStorageFileDnsZoneName + location: 'global' +} + +resource storageBlobDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: privateStorageBlobDnsZoneName + location: 'global' +} + +resource storageQueueDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: privateStorageQueueDnsZoneName + location: 'global' +} + +resource storageTableDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: privateStorageTableDnsZoneName + location: 'global' +} + +// -- Private DNS Zone Links -- +resource storageFileDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: storageFileDnsZone + name: '${storageFileDnsZone.name}-link' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } +} + +resource storageBlobDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: storageBlobDnsZone + name: '${storageBlobDnsZone.name}-link' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } +} + +resource storageTableDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: storageTableDnsZone + name: '${storageTableDnsZone.name}-link' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } +} + +resource storageQueueDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: storageQueueDnsZone + name: '${storageQueueDnsZone.name}-link' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: virtualNetwork.id + } + } +} + +// -- Private DNS Zone Groups -- +resource storageFilePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-02-01' = { + parent: storageFilePrivateEndpoint + name: 'filePrivateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [ + { + name: 'config' + properties: { + privateDnsZoneId: storageFileDnsZone.id + } + } + ] + } +} + +resource storageBlobPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-02-01' = { + parent: storageBlobPrivateEndpoint + name: 'blobPrivateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [ + { + name: 'config' + properties: { + privateDnsZoneId: storageBlobDnsZone.id + } + } + ] + } +} + +resource storageTablePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-02-01' = { + parent: storageTablePrivateEndpoint + name: 'tablePrivateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [ + { + name: 'config' + properties: { + privateDnsZoneId: storageTableDnsZone.id + } + } + ] + } +} + +resource storageQueuePrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-02-01' = { + parent: storageQueuePrivateEndpoint + name: 'tablePrivateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [ + { + name: 'config' + properties: { + privateDnsZoneId: storageQueueDnsZone.id + } + } + ] + } +} + +// -- Private Endpoints -- +resource storageFilePrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-02-01' = { + name: privateEndpointStorageFileName + location: location + properties: { + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, privateEndpointSubnetName) + } + privateLinkServiceConnections: [ + { + name: 'MyStorageFilePrivateLinkConnection' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + 'file' + ] + } + } + ] + } +} + +resource storageTablePrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-02-01' = { + name: privateEndpointStorageTableName + location: location + properties: { + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, privateEndpointSubnetName) + } + privateLinkServiceConnections: [ + { + name: 'MyStorageTablePrivateLinkConnection' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + 'table' + ] + } + } + ] + } +} + +resource storageQueuePrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-02-01' = { + name: privateEndpointStorageQueueName + location: location + properties: { + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, privateEndpointSubnetName) + } + privateLinkServiceConnections: [ + { + name: 'MyStorageQueuePrivateLinkConnection' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + 'queue' + ] + } + } + ] + } +} + +resource storageBlobPrivateEndpoint 'Microsoft.Network/privateEndpoints@2021-02-01' = { + name: privateEndpointStorageBlobName + location: location + properties: { + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, privateEndpointSubnetName) + } + privateLinkServiceConnections: [ + { + name: 'MyStorageBlobPrivateLinkConnection' + properties: { + privateLinkServiceId: storageAccount.id + groupIds: [ + 'blob' + ] + } + } + ] + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { + name: functionStorageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + networkAcls: { + bypass: 'None' + defaultAction: 'Deny' + } + } +} + +resource functionContentShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-04-01' = { + name: '${storageAccount.name}/default/${functionContentShareName}' +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: applicationInsightsName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + } +} + +resource plan 'Microsoft.Web/serverfarms@2021-01-01' = { + location: location + name: functionAppPlanName + sku: { + name: functionAppPlanSku + tier: 'ElasticPremium' + size: functionAppPlanSku + family: 'EP' + } + kind: 'elastic' + properties: { + maximumElasticWorkerCount: 20 + reserved: isReserved + } +} + +resource functionApp 'Microsoft.Web/sites@2021-01-01' = { + location: location + name: functionAppName + kind: isReserved ? 'functionapp,linux' : 'functionapp' + dependsOn: [ + storageFilePrivateDnsZoneGroup + storageBlobPrivateDnsZoneGroup + storageQueuePrivateDnsZoneGroup + storageTablePrivateDnsZoneGroup + + storageBlobDnsZoneLink + storageQueueDnsZoneLink + storageTableDnsZoneLink + storageQueueDnsZoneLink + + functionContentShare + ] + properties: { + serverFarmId: plan.id + reserved: isReserved + siteConfig: { + functionsRuntimeScaleMonitoringEnabled: true + linuxFxVersion: isReserved ? 'dotnet|3.1' : json('null') + appSettings: [ + { + name: 'APPINSIGHTS_INSTRUMENTATIONKEY' + value: '${appInsights.properties.InstrumentationKey}' + } + { + name: 'AzureWebJobsStorage' + value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' + } + { + name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' + value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' + } + { + name: 'FUNCTIONS_EXTENSION_VERSION' + value: '~3' + } + { + name: 'FUNCTIONS_WORKER_RUNTIME' + value: 'dotnet' + } + { + name: 'WEBSITE_VNET_ROUTE_ALL' + value: '1' + } + { + name: 'WEBSITE_CONTENTOVERVNET' + value: '1' + } + { + name: 'WEBSITE_CONTENTSHARE' + value: functionContentShareName + } + ] + } + } +} + +resource planNetworkConfig 'Microsoft.Web/sites/networkConfig@2021-01-01' = { + parent: functionApp + name: 'virtualNetwork' + properties: { + subnetResourceId: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetwork.name, functionSubnetName) + swiftSupported: true + } +} diff --git a/quickstarts/microsoft.web/function-app-storage-private-endpoints/metadata.json b/quickstarts/microsoft.web/function-app-storage-private-endpoints/metadata.json index fcce0528cd1c..ba41c430c208 100644 --- a/quickstarts/microsoft.web/function-app-storage-private-endpoints/metadata.json +++ b/quickstarts/microsoft.web/function-app-storage-private-endpoints/metadata.json @@ -1,10 +1,10 @@ { "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", "itemDisplayName": "Create Function App and private endpoint-secured Storage", - "description": "This template allows you to deploy an Azure Function App that communicates with Azure Storage over private endpoints.", - "summary": "This template allows you to deploy Azure Function App that communicates with Azure Storage over private endpoints.", + "description": "This template allows you to deploy an Azure Function App that communicates with Azure Storage over private endpoints.", + "summary": "This template allows you to deploy an Azure Function App that communicates with Azure Storage over private endpoints.", "githubUsername": "gabesmsft", - "dateUpdated": "2021-05-11", + "dateUpdated": "2021-07-21", "type": "QuickStart", "environments": [ "AzureCloud"