Search code examples
azureazure-functionsazure-resource-managerazure-bicep

How to update app settings of the Azure function app with a reference to the storage and key vault? - with bicep and modules


Background

The idea is to have a small "workload" / "pipeline" with a function app used for calling some 3rd party API at scheduled intervals and writing the response to a blob storage.

So in terms of resources I need / want to have:

  • storage for function app
  • function app with app settings linking it to the above mentioned storage
  • storage for data i.e. api responses
  • a keyvault for storing the key to the storage for data as a secret, with access policies allowing function app to access the keyvault secrets
  • the updated settings to the function app which will include the reference to the keyvault secret

I pretty much want to have a reusable template, so I am trying to use bicep modules. This is so far the structure of my IaC project:

./main.bicep
./modules/AppServicePlan/ConsumptionPlan.bicep
./modules/FunctionApp/FunctionApp.bicep
./modules/KeyVault/KeyVault.bicep
./modules/KeyVault/Secret.bicep
./modules/Storage/StorageAccount.bicep

So, I am using StorageAccount.bicep to create storage for data (from main.bicep as a module) as well as storage for function (from FunctionApp.bicep as a module). FunctionApp.bicep is also creating an app service plan resource, and also assigns initial app settings (i.e. AzureWebJobsStorage setting linking it to the storage for function).

I am also proceeding in a similar fashion with the keyvault. So I create a keyvault from main.bicep as a module, passing in as parameter the object id of the function app so that it is allowed in the key vault access policies to get and list secrets.

Issue

Finally I am trying to update the app settings in the function app, to include reference to the secret in the keyvault. First I was trying the approach to "extend" with the new app settings as described in the Is there a workaround to keep app settings which not defined in Bicep template? I could not get it working. So now I am trying with a simpler approach, to replace the app settings entirely. However I am receiving an error saying the storage for function resource is not found (?)

"error\": {\r\n    \"code\": \"ResourceNotFound\",\r\n    \"message\": \"The Resource 'Microsoft.Storage/storageAccounts/<storageForFuncName>' under resource group '<resourceGroup>' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix\"\r\n  }\r\n}"}

If I look in the deployment, all the sub-deployments including the one for storage for function succeeded, and it is only the main one that failed with the above error details. And in fact all the resources, apart from the updated app settings are created. Even the storage for function in question, and even the initial app settings that I create as part of the FunctionApp.bicep, which by the way contains exactly the same AzureWebJobsStorage key-value, that gives problem in main.bicep when I try to create the updated app settings.

How could I fix this? Or should I change my approach with modules entirely?

(By the way, this is my first IaC project, I am new to bicep, but I am already pretty annoyed I need to do things like having to effectively re-declare resources using resource and existing, even though that were declared with module, while also having to specifically mention the api version and type of resource. Just venting my frustration... ;) )

Things I tried

  1. I tested deploying with empty initial app settings (i.e. app settings as part of the FunctionApp.bicep) - I got the same outcome.

  2. Every resource I created as module I am declaring again as resource and using existing keyword, and adding it to the dependencies of the appsettings:

resource functionApp1AppSettings 'Microsoft.Web/sites/config@2022-09-01' = {
  kind: 'string'
  name: 'appsettings'
  parent: functionApp1r // resource //https://github.com/Azure/bicep/issues/7328

  properties: {
    // these settings were created at the point of function app creation with the `FunctionApp.bicep`
    AzureWebJobsFeatureFlags: 'EnableWorkerIndexing'
    // the below line is the problematic one and the one giving error
    AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount4funcName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount4func1r.listKeys().keys[0].value}'
    FUNCTIONS_EXTENSION_VERSION: '~4'
    FUNCTIONS_WORKER_RUNTIME: 'python'
    WEBSITE_TIME_ZONE: 'Europe/Amsterdam'
    // this is the "new" setting we are trying to update the appsettings with
    KV_SOME_SECRET: '@Microsoft.KeyVault(VaultName=${keyVaultName};SecretName=${secret.name})'
  }

  dependsOn: [
    functionApp1 //module
    kv //module
    kvr //existing resource of the above
    storageAccounts4func //module
    storageAccount4func1r //existing resource of the above
  ]
}

This did not help.

  1. I tried to put in my main.bicep the following:
var dummy = storageAccount4func1r.identity.principalId
output dummy string = dummy

as per the advice in: https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-not-found?tabs=bicep#solution-4-get-managed-identity-from-resource

but that did not help.


Solution

  • You can't declare the storage account resource and then use the existing function to retrieve the same storage account in the same bicep file. The error you are getting is likely from the existing function, because you cannot set a dependency for that. (Ie: It's trying to reference a resource that doesn't exist yet).

    Try declaring the storage account in a level above your appSettings, then pass through the StorageAccount name to your appSettings bicep file. Then you can use the existing function there to retrieve your Storage Account reference and it should work.

    If you post your bicep files it might also be easier to assist.