将 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING 设置为防火墙后的存储时,具有 VNet 集成的函数应用部署失败

Function App with VNet Integration Failing Deployment When Setting WEBSITE_CONTENTAZUREFILECONNECTIONSTRING to Storage Behind Firewall

部署以下 ARM 模板:虚拟网络、网络安全组、存储帐户、应用服务计划、功能应用

当省略 WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGWEBSITE_CONTENTSHARE 的设置(注释掉)时,部署成功但函数应用程序配置显示警告。

启用这两个设置时,部署失败并显示 403 禁止消息。

New-AzResourceGroupDeployment : 17:04:05 - The deployment '20201209-170356' failed with error(s). Showing 1 out of 1 error(s).
Status Message: There was a conflict. The remote server returned an error: (403) Forbidden. (Code: BadRequest)
- There was a conflict. The remote server returned an error: (403) Forbidden. (Code:)
-  (Code:BadRequest)
-  (Code:)
CorrelationId: ec11767b-9f8f-4722-acca-e751e5c1bbe8

我在 NSG 上尝试过多种设置,添加服务标签,允许 IP 与函数应用相关联。我还尝试在存储帐户防火墙上允许 IPRules。唯一有效的设置是使用 'Allow access from all networks' 完全禁用存储帐户防火墙,这对于网络来说是不可接受的设置。

演示错误的ARM模板:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
  },
  "variables": {
    "vnetName": "vnet1a",
    "addressPrefixVnet": "10.17.0.0/20",
    "addressPrefixSubnet": "10.17.4.0/24",
    "nsgName_sb_functionapp": "[concat(variables('vnetName'), '-sb-functionapp-nsg')]",
    "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'sa1a')]",
    "appServicePlanName": "[concat(uniquestring(resourceGroup().id), 'asp1a')]",
    "functionAppName": "[concat(uniquestring(resourceGroup().id), 'asp1a')]"
  },
  "resources": [
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2019-11-01",
      "name": "[variables('nsgName_sb_functionapp')]",
      "location": "[resourceGroup().location]",
      "tags": {
        "Purpose": "Function App"
      },
      "properties": {
        "securityRules": []
      }
    }, 
    {
      "type": "Microsoft.Network/virtualNetworks",
      "apiVersion": "2019-11-01",
      "name": "[variables('vnetName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_sb_functionapp'))]"
      ],
      "tags": {
        "Purpose": "Debug Function App and Storage Account Connectivity"
      },
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "[variables('addressPrefixVnet')]"
          ]
        },
        "subnets": [
          {
            "name": "sb-functionapp",
            "properties": {
              "addressPrefix": "[variables('addressPrefixSubnet')]",
              "networkSecurityGroup": {
                "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName_sb_functionapp'))]"
              },
              "serviceEndpoints": [
                {
                  "service": "Microsoft.Storage",
                  "locations": [
                    "*"
                  ]
                }
              ],
              "delegations": [
                {
                  "name": "delegation",
                  "properties": {
                    "serviceName": "Microsoft.Web/serverFarms"
                  }
                }
              ],
              "privateEndpointNetworkPolicies": "Enabled",
              "privateLinkServiceNetworkPolicies": "Enabled"
            }
          }
        ],
        "enableDdosProtection": false,
        "enableVmProtection": false
      }
    },
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2019-04-01",
      "name": "[variables('storageAccountName')]",
      "location": "[resourceGroup().location]",
      "tags": {
        "Purpose": "Debug Function App and Storage Account Connectivity"
      },
      "kind": "StorageV2",
      "sku": {
        "name": "Standard_GRS",
        "tier": "Standard"
      },
      "properties": {
        "networkAcls": {
          "defaultAction": "Deny",
          "bypass": "AzureServices",
          "supportsHttpsTrafficOnly": true,
          "ipRules": [],
          "encryption": {
            "keySource": "Microsoft.Storage",
            "services": {
              "file": {
                "enabled": true
              },
              "blob": {
                "enabled": true
              }
            }
          },
          "accessTier": "Hot",
          "virtualNetworkRules": [
            {
              "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('vnetName')), '/subnets/sb-functionapp')]",
              "ignoreMissingVNetServiceEndpoint": false
            }
          ]
        }
      }
    },
    {
      "type": "Microsoft.Web/serverfarms",
      "apiVersion": "2018-02-01",
      "name": "[variables('appServicePlanName')]",
      "location": "[resourceGroup().location]",
      "tags": {
        "Purpose": "Debug Function App and Storage Account Connectivity"
      },
      "sku": {
        "name": "EP1",
        "tier": "ElasticPremium",
        "size": "EP1",
        "family": "EP",
        "capacity": 1
      },
      "kind": "elastic",
      "properties": {
        "perSiteScaling": false,
        "maximumElasticWorkerCount": 20,
        "isSpot": false,
        "reserved": false,
        "isXenon": false,
        "hyperV": false,
        "targetWorkerCount": 0,
        "targetWorkerSizeId": 0
      }
    },
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2018-11-01",
      "name": "[variables('functionAppName')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
      ],
      "tags": {
        "Purpose": "Debug Function App and Storage Account Connectivity"
      },
      "kind": "functionapp",
      "properties": {
        "enabled": true,
        "hostNameSslStates": [
          {
            "name": "[concat(variables('functionAppName'), '.azurewebsites.net')]",
            "sslState": "Disabled",
            "hostType": "Standard"
          },
          {
            "name": "[concat(variables('functionAppName'), '.scm.azurewebsites.net')]",
            "sslState": "Disabled",
            "hostType": "Repository"
          }
        ],
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
        "reserved": false,
        "isXenon": false,
        "hyperV": false,
        "scmSiteAlsoStopped": false,
        "clientAffinityEnabled": true,
        "clientCertEnabled": false,
        "hostNamesDisabled": false,
        "containerSize": 1536,
        "dailyMemoryTimeQuota": 0,
        "httpsOnly": true,
        "redundancyMode": "None",
        "siteConfig": {
          "appSettings": [
            {
              "name": "FUNCTIONS_EXTENSION_VERSION",
              "value": "~1"
            },
            {
              "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
              "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-04-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTSHARE",
              "value": "[variables('functionAppName')]"
            },
            {
              "name": "WEBSITE_DNS_SERVER",
              "value": "168.63.129.16"
            },
            {
              "name": "WEBSITE_VNET_ROUTE_ALL",
              "value": "1"
            }
          ]
        }
      },
      "resources": [
        {
          "type": "networkConfig",
          "apiVersion": "2018-11-01",
          "name": "virtualNetwork",
          "location": "[resourceGroup().location]",
          "dependsOn": [
            "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
          ],
          "properties": {
            "subnetResourceId": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('vnetName')), '/subnets/sb-functionapp')]",
            "swiftSupported": true
          }
        }
      ]
    },
    {
      "type": "Microsoft.Web/sites/config",
      "apiVersion": "2018-11-01",
      "name": "[concat(variables('functionAppName'), '/web')]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
      ],
      "tags": {
        "Purpose": "Debug Function App and Storage Account Connectivity"
      },
      "properties": {
        "numberOfWorkers": 1,
        "defaultDocuments": [
          "Default.htm",
          "Default.html",
          "Default.asp",
          "index.htm",
          "index.html",
          "iisstart.htm",
          "default.aspx",
          "index.php"
        ],
        "netFrameworkVersion": "v4.0",
        "phpVersion": "5.6",
        "requestTracingEnabled": false,
        "remoteDebuggingEnabled": false,
        "remoteDebuggingVersion": "VS2019",
        "httpLoggingEnabled": false,
        "logsDirectorySizeLimit": 35,
        "detailedErrorLoggingEnabled": false,
        "publishingUsername": "[concat('$', variables('functionAppName'))]",
        "scmType": "VSTSRM",
        "use32BitWorkerProcess": true,
        "webSocketsEnabled": false,
        "alwaysOn": false,
        "managedPipelineMode": "Integrated",
        "virtualApplications": [
          {
            "virtualPath": "/",
            "physicalPath": "site\wwwroot",
            "preloadEnabled": true
          }
        ],
        "loadBalancing": "LeastRequests",
        "experiments": {
          "rampUpRules": [
          ]
        },
        "autoHealEnabled": false,
        "cors": {
          "allowedOrigins": [],
          "supportCredentials": false
        },
        "localMySqlEnabled": false,
        "ipSecurityRestrictions": [],
        "scmIpSecurityRestrictions": [
          {
            "ipAddress": "Any",
            "action": "Allow",
            "priority": 1,
            "name": "Allow all",
            "description": "Allow all access"
          }
        ],
        "scmIpSecurityRestrictionsUseMain": false,
        "http20Enabled": false,
        "minTlsVersion": "1.2",
        "ftpsState": "AllAllowed",
        "reservedInstanceCount": 1
      }
    }
  ]
}

部署到现有资源组的命令:

New-AzResourceGroupDeployment -Name (Get-Date).ToString('yyyyMMdd-HHmmss') -ResourceGroupName 'Test-FunctionApp-Storage-VNet' -TemplateFile .\DebugFunctionApp.json -Verbose

我在 看到了 question/answer 但它没有解决我看到的问题。

解决方案是添加另一个名为 WEBSITE_CONTENTOVERVNET 的设置并将值设置为 "1"

更新后的 appSettings 部分如下所示:

        "siteConfig": {
          "appSettings": [
            {
              "name": "FUNCTIONS_EXTENSION_VERSION",
              "value": "~1"
            },
            {
              "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
              "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-04-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTOVERVNET",
              "value": "1"
            },
            {
              "name": "WEBSITE_CONTENTSHARE",
              "value": "[variables('functionAppName')]"
            },
            {
              "name": "WEBSITE_DNS_SERVER",
              "value": "168.63.129.16"
            },
            {
              "name": "WEBSITE_VNET_ROUTE_ALL",
              "value": "1"
            }
          ]
        }

设置是https://docs.microsoft.com/en-us/azure/azure-functions/functions-app-settings#website_contentovervnet

的文档

For Premium plans only. A value of 1 enables your function app to scale when you have your storage account restricted to a virtual network. You should enable this setting when restricting your storage account to a virtual network.