Close

Expose Azure Function via API Management

When exposing an Azure Function via API Management, there are a few things you will have to take into account. First of all, note that each function has a function key that can be dynamically retrieved via function listsecrets. Go to underlying website to test the function listsecrets:

https://docs.microsoft.com/en-us/rest/api/appservice/webapps/listfunctionsecrets
To make this function call work, you will have to go to the function container in the Azure Portal. Go to Configuration, Application Settings and add setting AzureWebJobsSecretStorageType=Files.

Back on the docs.microsoft.com testsite enter: FunctionName=FunctionName, Name=FunctionAppName, resourcegroup, subscription. Post the request:

POST https://management.azure.com/subscriptions/8dc0ae41-5e05-479d-ac29-e70be7381555/resourceGroups/ipaas-np-rg/providers/Microsoft.Web/sites/ipaas-retryhelper-o-afu/functions/SendToParkingQueue/listsecrets?api-version=2019-08-01
Response:

{
  "key": "noea2hJpK...Sg==",
  "trigger_url": "https://functionapp.azurewebsites.net/api/function?code=noea2hJpK...Sg=="
}

Now, in the ARM we can use the following property schema to add a named value for the function key to API Management. Here we see function listsecrets again. Set ServiceName equal to the API Management instance. APIName is equal to the name of the API Management Service. Function key starts with the name of the function. Environment is equal to Dev/Test/Acc/Prod.

"resources": [
{
"type": "Microsoft.ApiManagement/service/properties",
"name": "[concat(parameters('serviceName'), '/', parameters('apiName'), '-functionkey-', variables('env'))]",
"apiVersion": "2019-01-01",
"properties": {
"displayName": "[concat(parameters('apiName'), '_functionkey', variables('env'))]",
"value": "[listsecrets(resourceId(parameters('backendResourceGroupName'),'Microsoft.Web/sites/functions', parameters('FunctionAppName'), parameters('FunctionName')),'2015-08-01').key]",
"tags": [
"CDHTriggerService"
],
"secret": true
}
}
]

We can use this named value in the ARM backend definition:

"resources": [
        {
            "type": "Microsoft.ApiManagement/service/backends",
            "name": "[concat(parameters('serviceName'), '/', parameters('apiName'), '-v1-', variables('env'))]",
            "apiVersion": "2019-01-01",
            "properties": {
                "title": null,
                "description": "Reference to backend function",
                "url": "[concat('https://',parameters('FunctionAppName'),'.azurewebsites.net/api')]",
                "protocol": "http",
                "resourceId": "[concat('https://management.azure.com/','subscriptions/',subscription().subscriptionId,'/resourceGroups/',parameters('backendResourceGroupName'),'/providers/Microsoft.Web/sites/',parameters('FunctionAppName'))]",
                "credentials": {
                    "header": {
                        "x-functions-key": [
                            "[concat('{{apiname-functionkey-', variables('env'),'}}')]"
                        ]
                    }
                }
            },
            "resources": [],
            "dependsOn": []
        }
    ]

When we deploy the function, we will once again run into the issue that function secrets requires an extra setting AzureWebJobsSecretStorageType=Files. The DevOps release pipeline will give the following error:

{
    "error": {
        "code": "Conflict",
        "message": "System.InvalidOperationException: Runtime keys are stored on blob storage. This API doesn't support this configuration. 
Please change Environment variable AzureWebJobsSecretStorageType value to 'Files'. 
...
    }
}

To resolve this error, add the required application setting to your FunctionApp definition (FunctionApp.json):

{
      "apiVersion": "2018-11-01",
      "type": "Microsoft.Web/sites",
      "name": "[variables('functionAppName')]",
      "tags": {
        "CostCenter": "[parameters('resourceTagCostCenter')]",
        "Platform": "[parameters('resourceTagPlatform')]"
      },

      "location": "[parameters('location')]",
      "kind": "functionapp",
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountFunction'))]"
      ],
      "properties": {
        "httpsOnly": "true",
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
        "siteConfig": {
          "appSettings": [
            ...
            {
                "name": "AzureWebJobsSecretStorageType",
                "value": "Files"
            },
	    ...