0
votes

I have an ARM template which creates a Storage Account, App Service Plan, Application Insights and a Function App. In the dependsOn section of the Function App, I configured it to be depending on the other three resources. But when I deploy the template, it fails with the following error:

Status Message: The Resource 'Microsoft.Storage/storageAccounts/dummystorage' under resource group 'dummy-rg' was not found.

I see that the Storage Account, ASP and App Insights are being created first, followed by the Function App. So it seems the dependency is being honored. It's almost like the Storage Account provisioning is not completely finished when the Function App is being created. As you can see in the template below, the Function App resource uses the listKeys to get the Storage Account keys as part of the provisioning.

Does anybody have any idea how I can prevent this from happening? I've read everything in the docs regarding dependencies, and I believe this should just work.

One remark is that the storage account is being deployed based on a condition. I can't imagine it has something to do with my issue, but I just wanted to mention it.

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "functionAppName": {
            "type": "string"
        },
        "appServicePlanName": {
            "type": "string"
        },
        "appInsightsName": {
            "type": "string"
        },
        "deployStorage": {
            "type": "bool",
            "defaultValue": true
        },
        "storageAccountName": {
            "type": "string"
        },
        "utcDateTime": {
            "type": "string",
            "defaultValue": "[utcNow()]"
        }
    },
    "variables": {
        "storageAccountDeploymentName": "[concat('storageDeploy-', parameters('utcDateTime'))]",
    },
    "resources": [
        {
            "name": "[parameters('functionAppName')]",
            "apiVersion": "2018-11-01",
            "type": "Microsoft.Web/sites",
            "kind": "functionapp",
            "location": "[parameters('location')]",
            "tags": "[parameters('tags')]",
            "dependsOn": [
                "[parameters('appInsightsName')]",
                "[parameters('appServicePlanName')]",
                "[resourceId('Microsoft.Resources/deployments', variables('storageAccountDeploymentName'))]"
            ],
            "properties": {
                "name": "[parameters('functionAppName')]",
                "siteConfig": {
                    "appSettings": [
                        {
                            "name": "FUNCTIONS_EXTENSION_VERSION",
                            "value": "~3"
                        },
                        {
                            "name": "FUNCTIONS_WORKER_RUNTIME",
                            "value": "powershell"
                        },
                        {
                            "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                            "value": "[reference(concat('Microsoft.Insights/components/', parameters('appInsightsName')), '2015-05-01').InstrumentationKey]"
                        },
                        {
                            "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
                            "value": "[reference(concat('Microsoft.Insights/components/', parameters('appInsightsName')), '2015-05-01').ConnectionString]"
                        },
                        {
                            "name": "AzureWebJobsStorage",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]"
                        },
                        {
                            "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]"
                        },
                        {
                            "name": "WEBSITE_CONTENTSHARE",
                            "value": "[concat(toLower(parameters('functionAppName')), '813b')]"
                        }
                    ],
                    "use32BitWorkerProcess": false,
                    "powerShellVersion": "[variables('powerShellVersion')]"
                },
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
                "clientAffinityEnabled": true
            }
        },
        {
            "name": "[parameters('appServicePlanName')]",
            "apiVersion": "2018-11-01",
            "type": "Microsoft.Web/serverfarms",
            "location": "[parameters('location')]",
            "tags": "[parameters('tags')]"
            // snip
        },
        {
            "name": "[parameters('appInsightsName')]",
            "apiVersion": "2020-02-02-preview",
            "type": "microsoft.insights/components",
            "location": "[parameters('location')]",
            "tags": "[parameters('tags')]",
            // snip
        },
        {
            "name": "[variables('storageAccountDeploymentName')]",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2019-10-01",
            "condition": "[parameters('deployStorage')]",
            "properties": {
                "expressionEvaluationOptions": {
                    "scope": "outer"
                },
                "mode": "Incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "resources": [
                        {
                            "name": "[parameters('storageAccountName')]",
                            "type": "Microsoft.Storage/storageAccounts",
                            "apiVersion": "2019-06-01",
                            "tags": "[parameters('tags')]",
                            "location": "[parameters('location')]",
                            // snip
                        }
                    ]
                }
            }
        }
    ]
}
3

3 Answers

2
votes

I think you're running into this: https://bmoore-msft.blog/2020/07/26/resource-not-found-dependson-is-not-working/

TLDR; listKeys is called in a separate job in the deployment engine and gets called early if that resource (i.e. your storage) is not in the same deployment.

  1. you don't need to nest your storageAccount deployment, doesn't hurt but doesn't help. If your storageAccount deployment is unconditional, then it will help because ARM will wait before running listKeys.
  2. if you need the condition, the only fix is to nest the function app deployment and set a dependency on the conditional storageAccount (the dependency need not be conditional ARM will handle that). You need to set the evaluation scope to "inner" on that nested deployment.

That help?

0
votes

I noticed your dependsOn for Storage looks like this:

"dependsOn": [
            "[parameters('appInsightsName')]",
            "[parameters('appServicePlanName')]",
            "[resourceId('Microsoft.Resources/deployments', variables('storageAccountDeploymentName'))]"

Whereas my despendsOn for Storage Account and Blob Container looks like this... could that be the reason it's not working? I have never used Microsoft.Resources/deployments (FYI my ARM Template deployment is working)

"dependsOn": [
            "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('StorageAccountName'), 'default', variables('BlobContainerName2'))]",
            "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
0
votes

Alternatively:

You could drop the conditional and deploy the Storage Account each time. If it exists no change, if it doesn't exist then it's created.

The only caveat to this is if you change something on the storage account post-deployment or if the Storage account has a dynamically set name based on Timestamp or GUID etc.

I realize this is a work around, but it should solve your problem barring the caveat above.