Search code examples
azure-functionsazureservicebus.net-5

ServiceBusTrigger: local.settings to "cloud" settings


After several tries, we were able to run a ServiceBusTrigger function from a local machine.

But when we deploy to production, it doesn't work. I think it can be any of the 3 following problems:

1) For serviceBus triggers the doc says that you can use whatever "servicebusConnectionString" while put the same name in the trigger attribute, as:

Function signature

After lots of testing we found that it only works if you put inside "values" in local.settings.json as:

{
  { "values": 
    { "ServicebusConnectionString": "[my SB connStr here]" } 
  }
}

Now, the problem is what do we have to put in the published function to work

  • Just ServicebusConnectionString?
  • Values_ServicebusConnectionString?
  • Values.ServicebusConnectionString?
  • VALUES_SERVICEBUS_CONNECTION_STRING?

Settings value for SB

2) Also, we have local.settings.json with another setting like:

{
  "clients": { "mapId": "AZ45" } 
}

What we should put in the Azure Portal settings for the deployed function?

  • clients_map_id?
  • clients.mapId?
  • CLIENTS_MAP_ID?

sample value

3) Our function is developed in .Net5, but Azure Portal only allows to create the function service either with .Net Core 3.5 or .Net6. We picked .Net6, assuming that it will have backward compatibility. I don't think this may cause a problem, but who knows.

I want to highlight that everything works fine when running in debug from Visual Studio. All the settings are read and the trigger is fired as expected.

After deploy, we don't see any error on the logs, but the messages are not being consumed in our ServiceBus unless we run the function in a local machine. So we guess the function is just not starting due to misconfiguration. I'm pretty confused, thanks for any guidance!

Edit: It's incredible how natural seems, to many people, to switch from "values:configValue" in local to just "configValue" in production. I must be missing something, but for me, a key aspect in configuration is to preserve the "keys" (as values:configValue) but change the contents according to the environment. For example, if the key is "values:configValue" in development, it should be the same in PROD


Solution

  • The local.settings.json file is supposed to be used for your local development experience. It is not meant for production. It is the convention that you will have to keep your custom settings entries inside the Values element. So you may store your service bus connection sting like below in the local.settings.json file.

    {
      "IsEncrypted": false,
      "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "ServiceBusConnectionString": "your-secret-conn-string-value-here"
      }
    }
    

    The Values settings inside the local.appsettings.json is meant to store key value pairs of string-string type. JSON objects or arrays are not supported.

    For your service bus connection string in production, you should just create an app setting with the same name you used in your code (The Connection attribute value of ServiceBusTrigger attribute. For example, if your function code is using is ServiceBusTrigger attribute like below

    [ServiceBusTrigger("mytopic","mysubscription", 
                     Connection = "ServicebusConnectionString")] object message
    

    For production(azure), you will create a new application setting entry where the name will be ServicebusConnectionString. The runtime will read this and use it. You do not need the Values prefix.

    By default, the local.settings.json file is not used a configuration source when you run your code in prod.

    EDIT: The .net isolated model(out of process model) gives you more flexibility to make it customizable for your needs since you are creating your host. For example, if you want to add another json file as your configuration source (similar to how aspnet core apps does it), you can do that in the Main method in your Program.cs where you are building the Host. Here is a quick example,

    public static Task Main()
    {
        var host = new HostBuilder()
            .ConfigureAppConfiguration((context,config ) =>
            {
                var env = context.HostingEnvironment;
    
                config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);
                config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
            })
            .ConfigureFunctionsWorkerDefaults()
            .Build();
    
        return host.RunAsync();
    }
    

    Make sure you update the file properties so that it gets copied to output directory.

    Now you can inject IConfiguration or use the options pattern to strongly type your config to a type and inject that to your classes.

    EDIT:

    To access nested config property value, you should use : as the separator character. For example, If you had added a custom JSON file like below as the configuration source.

    {
      "clients": { "mapId": "AZ45-prod" }
    }
    

    You could access it using an IConfiguration instance like this.

     var clientMapId = configuration["clients:mapId"];