Search code examples
azure-devopsazure-resource-manager

How to make a FunctionApp setting slotSpecific using ARM templates


We are using ARM templates to deploy function apps but the slotSetting: true property is not respected and I cannot find any modern documentation as to how to make app settings slot specific.

THis is my app settings snippet in my ARM template

{
              "name": "AzureWebJobs.HandleFiscalFrResponse.Disabled",
              "value": "1",
              "slotSetting": true
}

the setting and the value works but the slotSettings attribute is ignored silently, no error is shown its just ignored.

What is the correct way to make a function app setting slot specific?


Solution

    • I have reproduced the issue and able to resolve, please follow the below steps

    • Open VS code and create a file using .json extension and se the below code

    Thanks @patelchandni for the ARM Template code.

    My Filename.json

    {
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "functionAppName": {
          "type": "string",
          "defaultValue": "[format('tar-{0}', uniqueString(resourceGroup().id))]"
        },
        "storageAccountType": {
          "type": "string",
          "defaultValue": "Standard_LRS",
          "allowedValues": [
            "Standard_LRS",
            "Standard_GRS",
            "Standard_RAGRS"
          ]
        },
        "location": {
          "type": "string",
          "defaultValue": "[resourceGroup().location]"
        },
        "appInsightsLocation": {
          "type": "string",
          "defaultValue": "[resourceGroup().location]"
        },
        "functionWorkerRuntime": {
          "type": "string",
          "defaultValue": "node",
          "allowedValues": [
            "dotnet",
            "node",
            "python",
            "java"
          ]
        },
        "functionPlanOS": {
          "type": "string",
          "defaultValue": "Windows",
          "allowedValues": [
            "Windows",
            "Linux"
          ]
        },
        "functionAppPlanSku": {
          "type": "string",
          "defaultValue": "EP1",
          "allowedValues": [
            "EP1",
            "EP2",
            "EP3"
          ]
        },
        "linuxFxVersion": {
          "type": "string",
          "defaultValue": "",
          "metadata": {
            "description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
          }
        }
      },
      "variables": {
        "hostingPlanName": "[parameters('functionAppName')]",
        "applicationInsightsName": "[parameters('functionAppName')]",
        "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]",
        "isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]",
        "slotContentShareName": "[concat(parameters('functionAppName'), '-deployment')]"
      },
      "resources": [
        {
          "type": "Microsoft.Storage/storageAccounts",
          "apiVersion": "2021-02-01",
          "name": "[variables('storageAccountName')]",
          "location": "[parameters('location')]",
          "sku": {
            "name": "[parameters('storageAccountType')]"
          },
          "kind": "Storage"
        },
        {
          "type": "Microsoft.Web/serverfarms",
          "apiVersion": "2021-02-01",
          "name": "[variables('hostingPlanName')]",
          "location": "[parameters('location')]",
          "sku": {
            "tier": "ElasticPremium",
            "name": "[parameters('functionAppPlanSku')]",
            "family": "EP"
          },
          "properties": {
            "maximumElasticWorkerCount": 20,
            "reserved": "[variables('isReserved')]"
          },
          "kind": "elastic"
        },
        {
          "type": "microsoft.insights/components",
          "apiVersion": "2020-02-02",
          "name": "[variables('applicationInsightsName')]",
          "location": "[parameters('appInsightsLocation')]",
          "tags": {
            "[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
          },
          "properties": {
            "Application_Type": "web"
          },
          "kind": "web"
        },
        {
          "type": "Microsoft.Web/sites",
          "apiVersion": "2021-02-01",
          "name": "[parameters('functionAppName')]",
          "location": "[parameters('location')]",
          "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
          "dependsOn": [
            "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
            "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
            "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]"
          ],
          "properties": {
            "reserved": "[variables('isReserved')]",
            "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
            "siteConfig": {
              "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
              "appSettings": [
                {
                  "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                  "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey]"
                },
                {
                  "name": "AzureWebJobsStorage",
                  "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                },
                {
                  "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                  "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                },
                {
                  "name": "WEBSITE_CONTENTSHARE",
                  "value": "[toLower(parameters('functionAppName'))]"
                },
                {
                  "name": "FUNCTIONS_EXTENSION_VERSION",
                  "value": "~4"
                },
                {
                  "name": "FUNCTIONS_WORKER_RUNTIME",
                  "value": "[parameters('functionWorkerRuntime')]"
                },
                {
                  "name": "WEBSITE_NODE_DEFAULT_VERSION",
                  "value": "~14"
                }
              ]
            }
          }
        },
        {
          "type": "Microsoft.Web/sites/slots",
          "apiVersion": "2021-02-01",
          "name": "[concat(parameters('functionAppName'), '/deployment')]",
          "kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
          "location": "[parameters('location')]",
          "dependsOn": [
            "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
          ],
          "properties": {
            "reserved": "[variables('isReserved')]",
            "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
            "siteConfig": {
              "linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), json('null'))]",
              "appSettings": [
                {
                  "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                  "value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2015-05-01').InstrumentationKey]"
                },
                {
                  "name": "AzureWebJobsStorage",
                  "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                },
                {
                  "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                  "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                },
                {
                  "name": "WEBSITE_CONTENTSHARE",
                  "value": "[variables('slotContentShareName')]"
                },
                {
                  "name": "FUNCTIONS_EXTENSION_VERSION",
                  "value": "~4"
                },
                {
                  "name": "FUNCTIONS_WORKER_RUNTIME",
                  "value": "[parameters('functionWorkerRuntime')]"
                },
                {
                  "name": "WEBSITE_NODE_DEFAULT_VERSION",
                  "value": "~14"
                }
              ]
            }
          }
        }
      ]
    }
    
    
    • Click on the below marked one to create parameter file enter image description here

    • Click new as shown in below picture enter image description here

    • Use the below code in FileName.parameters.json

    {
    
      "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
        "functionAppName": {
          "value": "zapper01"
        },
        "storageAccountType": {
          "value": "Standard_LRS"
        },
        "location": {
          "value": "EastUS"
        },
        "appInsightsLocation": {
          "value": "EastUS"
        },
        "functionWorkerRuntime": {
          "value": "node"
        },
        "functionPlanOS": {
          "value": "Windows"
        },
        "functionAppPlanSku": {
          "value": "EP1"
        },
        "linuxFxVersion": {
          "value": "3.9"
        }
      }
    }
    
    
    • To login to azure portal, run the PowerShell command
      az login 
    
    • Set subscription by using
        az account set --subscription "Subscription ID xxxxxx-xxxxxxx-xxxxxxx-xxxxx"
    
    • Deploy to azure portal
        New-AzResourceGroupDeployment -ResourceGroupName "ResourceGroupName" -TemplateFile "FileName.json" -TemplateParameterFile "Filename.parameters.json"
    
    • After execution you will get below one as result in power shall enter image description here
    1. After deploying into azure portal open the function app and select deployment slot enter image description here

    Update

    • I have deployed the below code in Custom deployment In Azure portal

    • Thanks, @seligj95 for the ARM Template code.

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "parameters": {
            "baseResourceName": {
                "type": "string",
          "metadata": {
                    "description": "Name of the resource"
          },
          "maxLength": 15
            },
        "appSettingName": {
                "type": "string",
          "metadata": {
                    "description": "Name of the app setting"
          },
          "maxLength": 24
        },
        "environments": {
                "defaultValue": [
                  "Dev",
            "QA",
            "UAT",
            "Preview"
          ],
          "type": "array",
          "metadata": {
                    "description": "Array with the names for the environment slots"
          },
          "maxLength": 19
        },
        "location": {
                "type": "string",
          "defaultValue": "[resourceGroup().location]",
          "metadata": {
                    "description": "Location for all resources."
          }
            }
        },
      "variables": {
            "standardPlanMaxAdditionalSlots": 4,
        "webAppPortalName": "[concat(parameters('baseResourceName'), 'Portal')]",
        "appServicePlanName": "[concat('AppServicePlan-', parameters('baseResourceName'))]",
        "stickyAppSettingName": "[concat(parameters('appSettingName'), '-sticky')]"
      },
      "resources": [
        {
            "apiVersion": "2020-06-01",
          "type": "Microsoft.Web/serverfarms",
          "kind": "app",
          "name": "[variables('appServicePlanName')]",
          "location": "[parameters('location')]",
          "comments": "This app service plan is used for the web app and slots.",
          "tags": {
                "displayName": "AppServicePlan"
          },
          "properties": { },
          "sku": {
                "name": "[if(lessOrEquals(length(parameters('environments')), variables('standardPlanMaxAdditionalSlots')), 'S1', 'P1')]"
          }
        },
        {
            "apiVersion": "2020-06-01",
          "type": "Microsoft.Web/sites",
          "kind": "app",
          "name": "[variables('webAppPortalName')]",
          "location": "[parameters('location')]",
          "comments": "This is the web app, also the default 'nameless' slot.",
          "tags": {
                "displayName": "WebApp"
          },
          "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
            "siteConfig": {
                    "appSettings": [
                      {
                        "name": "[parameters('appSettingName')]",
                  "value": "value"
                      },
                {
                        "name": "[variables('stickyAppSettingName')]",
                  "value": "value"
                }
              ]
            }
            },
          "dependsOn": [
            "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
          ]
        },
        {
            "apiVersion": "2020-06-01",
          "type": "Microsoft.Web/sites/slots",
          "name": "[concat(variables('webAppPortalName'), '/', parameters('environments')[copyIndex()])]",
          "kind": "app",
          "location": "[parameters('location')]",
          "comments": "This specifies the web app slots.",
          "tags": {
                "displayName": "WebAppSlots"
          },
          "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
          },
          "dependsOn": [
            "[resourceId('Microsoft.Web/Sites', variables('webAppPortalName'))]"
          ],
          "copy": {
                "name": "webPortalSlot",
            "count": "[length(parameters('environments'))]"
          }
        },
        {
            "apiVersion": "2020-06-01",
          "name": "[concat(variables('webAppPortalName'), '/slotconfignames')]",
          "type": "Microsoft.Web/sites/config",
          "comments": "This specifies the sticky (slot setting) application settings.",
          "dependsOn": [
            "[resourceId('Microsoft.Web/Sites', variables('webAppPortalName'))]"
          ],
          "properties": {
                "appSettingNames": [
                  "[variables('stickyAppSettingName')]"
            ]
          }
        }
      ]
    }
    
    • I have selected the second slot as specific sticky slot in arm template, So the second slot is deployed

    enter image description here