Dive into ARM template from a Function App by info.odysseyx@gmail.com September 3, 2024 written by info.odysseyx@gmail.com September 3, 2024 0 comment 9 views 9 Create Function Apps (or other Azure resources) using ARM templates to organize related resources and highly customize some properties of these resources. However, if you do not understand the dependencies between Azure resources (for example, Function App depends on Storage Account), it is difficult to customize the template. This article briefly explains resource dependencies in ARM templates using Function App as an example. index Resource structure of a function app ARM’s dependOn property ARM’s runtime features ARM’s nested distribution Perfect example Resource structure of a function app In simple terms, a function app is an abstract concept that consists of an application, an app service plan, and storage. Their roles are as follows: Application: The function app itself, which is responsible for executing various custom triggers and fetching or storing relevant data from other services. App Service Plan: A collection of hardware resources that actually host your Function Apps. The serverless nature of a Function App is transparent to end users, but there are still instances that load and run these applications. Storage (i.e. storage account): Function apps use blobs/files to store application-related settings, actual code, and other data. From the above explanation, we can see that there is a specific order in which we need to create this set of resources. Storage and App Service Plans must be created before the Function App. ARM’s dependOn property Please refer to The following ARM template snippet: If we look closely at the process of creating a storage account, we will see that it is also an abstract concept that includes several services (e.g. blob, file, table, queue). The actual file is a more refined resource that needs to be created. Correctly, the file service must be created before the file share, and the order of creation is as follows: storage account before file service, before file share. That is, file share creation depends on the existence of a file service, and file service creation depends on the existence of a storage account. The so-called “depends on” is the dependOn attribute of the ARM template. In the above example, you can see the dependency behavior of these three resources. The description of dependOn can help you ensure the order of resource creation during the process, thus avoiding the problem of missing resources. ARM’s runtime features Can I use the same approach to specify that App Service Plan and Function App should be created after Storage? The answer is no.. The reason is that authorization and communication between Function App and Storage basically uses Connection String, which is randomly generated by the system while creating Storage. This makes it impossible (and insecure) for ARM template to pre-specify Storage’s Connection String. Instead, ARM uses functions like listKeys to dynamically retrieve the relevant Connection String from an already created Storage resource and use it as a parameter to the Function App to later communicate with Storage. See the following ARM template snippet. The problem is that the listKeys function is called a runtime function, and its execution order has the highest priority in the entire deployment. In the same deployment, the runtime function executes immediately and returns the result without considering the order of dependOn. For more information, see this article. Resource not found (dependsOn not working) – ARM Template Sandbox (bmoore-msft.blog) Is there any way to solve this problem? The answer is yes. The runtime functions mentioned above will only be executed first in the same deployment. You can avoid this problem by modifying the deployment to a nested form. ARM’s nested distribution Please refer to The following ARM template snippet: In addition to creating the following three resources from the original resource, you can also create other deployments, such as “Microsoft.Resources/deployments”. “Microsoft.Storage/storageAccounts” “Microsoft.Storage/storageAccounts/fileservices” “Microsoft.Storage/storageAccounts/fileServices/shares” Adding the dependOn attribute will cause all executions of ASPResourcesDeployment, including runtime functions, to be executed only after Storage has been created. Perfect example { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "resourcePrefix": { "type": "string", "defaultValue": "[concat('ringe', '-', uniqueString(newGuid()))]", "metadata": { "description": "Do not modify this name" } } }, "variables": { "storageAccountName": "[concat(replace(parameters('resourcePrefix'),'-', ''), 'sa')]", "storageAccountSkuName": "Standard_LRS", "storageAccountSupportsHttpsTrafficOnly": true, "storageAccountMinimumTlsVersion": "TLS1_2", "storageAccountDefaultToOAuthAuthentication": true, "storageAccountIsShareSoftDeleteEnabled": true, "storageAccountShareSoftDeleteRetentionDays": 7, "storageAccountFileShareName": "ringe-file", "storageAccountFileShareShareQuota": 5120, "storageAccountFileShareEnabledProtocols": "SMB", "planName": "[concat(parameters('resourcePrefix'), '-', 'plan')]", "planKind": "linux", "planSkuTier": "Dynamic", "planSkuName": "Y1", "planWorkerSize": "0", "planWorkerSizeId": "0", "planNumberOfWorkers": "1", "planReserved": true, "functionName": "[concat(parameters('resourcePrefix'), '-', 'func')]", "functionKind": "functionapp,linux", "functionSiteConfigAppSettingsFUNCTIONS_EXTENSION_VERSION": "~4", "functionSiteConfigAppSettingsFUNCTIONS_WORKER_RUNTIME": "node", "functionSiteConfigAppSettingsWEBSITE_CONTENTSHARE": "ringe-func", "functionSiteConfigUse32BitWorkerProcess": false, "functionSiteConfigFtpsState": "FtpsOnly", "functionSiteConfigLinuxFxVersion": "Node|18", "functionClientAffinityEnabled": false, "functionPublicNetworkAccess": "Enabled", "functionHttpsOnly": true, "functionServerFarmId": "[concat('subscriptions/', subscription().subscriptionId, '/resourcegroups/', resourceGroup().name, '/providers/Microsoft.Web/serverfarms/', variables('planName'))]", "scriptName": "[concat(replace(parameters('resourcePrefix'),'-', ''), 'sh')]" }, "resources": [ { "comments": "Storage Account", "apiVersion": "2022-05-01", "type": "Microsoft.Storage/storageAccounts", "location": "[resourceGroup().location]", "name": "[variables('storageAccountName')]", "tags": {}, "sku": { "name": "[variables('storageAccountSkuName')]" }, "properties": { "supportsHttpsTrafficOnly": "[variables('storageAccountSupportsHttpsTrafficOnly')]", "minimumTlsVersion": "[variables('storageAccountMinimumTlsVersion')]", "defaultToOAuthAuthentication": "[variables('storageAccountDefaultToOAuthAuthentication')]" } }, { "comments": "Storage Account: fileservices", "apiVersion": "2022-05-01", "type": "Microsoft.Storage/storageAccounts/fileservices", "name": "[concat(variables('storageAccountName'), '/default')]", "properties": { "shareDeleteRetentionPolicy": { "enabled": "[variables('storageAccountIsShareSoftDeleteEnabled')]", "days": "[variables('storageAccountShareSoftDeleteRetentionDays')]" } }, "dependsOn": [ "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]" ] }, { "comments": "Storage Account: fileshares", "apiVersion": "2021-04-01", "type": "Microsoft.Storage/storageAccounts/fileServices/shares", "location": "[resourceGroup().location]", "name": "[concat(variables('storageAccountName'), '/default/', variables('storageAccountFileShareName'))]", "dependsOn": [ "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('storageAccountName'), 'default')]", "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" ], "properties": { "shareQuota": "[variables('storageAccountFileShareShareQuota')]", "enabledProtocols": "[variables('storageAccountFileShareEnabledProtocols')]" } }, { "comments": "ASP nested resources due to Storage Account", "apiVersion": "2017-05-10", "type": "Microsoft.Resources/deployments", "name": "ASPResourcesDeployment", "dependsOn": [ "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]" ], "properties": { "mode": "Incremental", "template": { "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": {}, "variables": {}, "resources": [ { "comments": "ASP", "apiVersion": "2018-11-01", "type": "Microsoft.Web/serverfarms", "location": "[resourceGroup().location]", "name": "[variables('planName')]", "kind": "[variables('planKind')]", "sku": { "Tier": "[variables('planSkuTier')]", "Name": "[variables('planSkuName')]" }, "tags": {}, "dependsOn": [], "properties": { "name": "[variables('planName')]", "workerSize": "[variables('planWorkerSize')]", "workerSizeId": "[variables('planWorkerSizeId')]", "numberOfWorkers": "[variables('planNumberOfWorkers')]", "reserved": "[variables('planReserved')]" } }, { "comments": "Function App", "apiVersion": "2018-11-01", "type": "Microsoft.Web/sites", "location": "[resourceGroup().location]", "name": "[variables('functionName')]", "kind": "[variables('functionKind')]", "tags": {}, "dependsOn": [ "[concat('Microsoft.Web/serverfarms/', variables('planName'))]" ], "properties": { "name": "[variables('functionName')]", "siteConfig": { "appSettings": [ { "name": "FUNCTIONS_EXTENSION_VERSION", "value": "[variables('functionSiteConfigAppSettingsFUNCTIONS_EXTENSION_VERSION')]" }, { "name": "FUNCTIONS_WORKER_RUNTIME", "value": "[variables('functionSiteConfigAppSettingsFUNCTIONS_WORKER_RUNTIME')]" }, { "name": "AzureWebJobsStorage", "value": "[concat('DefaultEndpointsProtocol=https;AccountName=",variables("storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts',variables('storageAccountName')),'2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]" }, { "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", "value": "[concat('DefaultEndpointsProtocol=https;AccountName=",variables("storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts',variables('storageAccountName')),'2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]" }, { "name": "WEBSITE_CONTENTSHARE", "value": "[variables('functionSiteConfigAppSettingsWEBSITE_CONTENTSHARE')]" } ], "cors": { "allowedOrigins": [] }, "use32BitWorkerProcess": "[variables('functionSiteConfigUse32BitWorkerProcess')]", "ftpsState": "[variables('functionSiteConfigFtpsState')]", "linuxFxVersion": "[variables('functionSiteConfigLinuxFxVersion')]" }, "clientAffinityEnabled": "[variables('functionClientAffinityEnabled')]", "virtualNetworkSubnetId": null, "publicNetworkAccess": "[variables('functionPublicNetworkAccess')]", "httpsOnly": "[variables('functionHttpsOnly')]", "serverFarmId": "[variables('functionServerFarmId')]" } } ], "outputs": {} } } } ], "outputs": {} } You can publish this template using a simple method (you can replace “ringe” that appears in the template and CLI with your project name). az group create --name ringe-test-rg --location westus az deployment group create --resource-group ringe-test-rg --template-file ringe.json Source link Share 0 FacebookTwitterPinterestEmail info.odysseyx@gmail.com previous post Empowered to thrive: Jiarui’s Entrepreneurial Journey next post California Consumer Privacy Act (CCPA) Opt-Out Icon You may also like From Zero to Hero: Building Your First Voice Bot with GPT-4o Real-Time API using... October 12, 2024 A Guide to Responsible Synthetic Data Creation October 12, 2024 Capacity Template – MGDC for SharePoint October 11, 2024 Using Azure NetApp Files (ANF) for data- and logfiles for Microsoft SQL Server in... October 11, 2024 Microsoft Community – Do you love stickers?! Do you want to be a part... October 11, 2024 Advanced Alerting Strategies for Azure Monitoring October 11, 2024 Leave a Comment Cancel Reply Save my name, email, and website in this browser for the next time I comment.