Azure / AppConfiguration

Questions, feedback and samples for Azure App Configuration service
MIT License
240 stars 73 forks source link

Use App Configuration in Azure Functions Triggers #203

Open BigBd1 opened 4 years ago

BigBd1 commented 4 years ago

Hello,

In an Azure Function V2, is it possible to bind the connection string or the queue name of the QueueTrigger attribute to an Azure App Configuration Setting ?

Thanks for your help !

zhenlan commented 4 years ago

@BigBd1, with a quick look, I don't see an easy way to retrieve the QueueTrigger information from App Configuration. User code can retrieve configuration from App Configuration only at runtime but QueueTrigger attribute requires the information at build time.

This will be something we will have to take a deeper look and probably discuss with the Azure Functions team.

cc: @lisaguthrie @yegu-ms

lundmikkel commented 4 years ago

I'm trying to read the queue name from App Configuration as well, but without luck. And yes, it seems the problem is that settings are read at runtime only.

For ServiceBus connection strings, it is solved by having a standard name for the setting, i.e. AzureWebJobsServiceBus, which is known by Azure Functions and loaded outside your solution. It isn't a custom string loaded manually and provided to an extension function, as described here.

What is the current status? Is this something you expect to support?

JosXa commented 4 years ago

QueueTrigger attribute requires the information at build time.

It does require a string at build time, but by using a %Namespace:Key% identifier it will be loaded at runtime. I have a suspicion that it might be possible to somehow hook into the configuration system to provide this value 🤔 After all, the key is typically provided in a local.settings.json

zhenlan commented 4 years ago

@JosXa, the value is available at runtime, but Azure Functions need the value earlier than that. In the current design of Azure Functions, it's too late when the configuration is loaded from App Configuration at runtime.

JosXa commented 4 years ago

@BigBd1 Maybe we can generalize the issue title to Use App Configuration setting for Azure Function Triggers, as every dynamically configurable trigger is affected?

cvanama commented 3 years ago

Is there any resolution for this issue? I am looking any way to read the topicname,subscriptionname, ServiceBusConnectionString in ServiceBusTrigger from azure app configuration. Please let me know.

JulesP96 commented 3 years ago

@cvanama I stumble uppon this issue a few weeks ago as I wanted to use App Configuration for SignalR and CosmosDB parameters. This is actually simpler than it looks and you should be able to update the following code with a service bus.

First the Function itself:

public class CosmosSignalFunction
    {
        [FunctionName("function")]
        public async Task Run(
            [CosmosDBTrigger(
                databaseName: "%CosmosDB:DatabaseName%",
                collectionName: "%CosmosDB:CircleRepository:Name%",
                ConnectionStringSetting = "CosmosDB:ConnectionString",
                CreateLeaseCollectionIfNotExists = true,
                LeaseCollectionName = "leases")]IReadOnlyList<Document> input,
            [SignalR(
                HubName = "WishlistHub", 
                ConnectionStringSetting = "SignalR:ConnectionString")] IAsyncCollector<SignalRMessage> signalRMessages,
            ILogger log)
        {
            foreach (var entry in input)
            {
                // Do stuff
            }
        }
    }

As you can see, nothing much here, I'm just using bindings expression for my parameters. Cf: https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-expressions-patterns

The magic happens in the Startup class of the function (I assume that you use Azure Function ~2 or higer), this is where you inject all your services and dependencies.

 public class Startup : IWebJobsStartup
    {
        public IConfiguration Configuration { get; set; }

        public void Configure(IWebJobsBuilder builder)
        {
            var configurationBuilder = new ConfigurationBuilder();
            configurationBuilder.CreateDefaultAzureFunctionConfiguration();

            Configuration = configurationBuilder.Build();

            builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), Configuration));

            ConfigureService(builder.Services);
        }

        public IServiceCollection ConfigureService(IServiceCollection services)
        {
            // Options
            services.Configure<DefaultDocumentDbOptions>(
                Configuration.GetSection(DefaultDocumentDbOptions.SectionName));

            return services;
        }
    }

The important line is that one:

builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), Configuration));

This will replace the configuration injected through the App Settings by the Azure Function SDK by the configuration built with App Configuration. So when using the parameters bindings expressions you must refer to the App Configuration section of your parameter.

Btw, the configurationBuilder.CreateDefaultAzureFunctionConfiguration() is a custom extension method that manage the App Configuration with AddAzureAppConfiguration, there is nothing special there.

cvanama commented 3 years ago

@JulesP96 Thanks lot for your reply, i will try this approach and will get back to you if found any issues.

crhistianramirez commented 3 years ago

@JulesP96 this worked perfectly thank you.

One thing that tripped me up - notice that Connection string is not being replaced here and isn't surrounded by % it is literally the name of the setting that holds the connection string.

Also, while this works I do get the following warning

The Functions scale controller may not scale the following functions correctly because some configuration values were modified in an external startup class.

For my purposes this is not an issue because we are actually limiting the instances to specifically not scale (so downstream services aren't overwhelmed) but it would be great for azure app configuration to be supported first-class by azure functions.

zhenlan commented 3 years ago

@JulesP96 thanks for sharing. Just add a note for others who may come across this thread.

The Function App sample in this repo is updated to include this https://github.com/Azure/AppConfiguration/blob/main/examples/DotNetCore/AzureFunction/FunctionApp/ReadQueuedMessage.cs

beukerz commented 2 years ago

Is there also a solution for this problem in case a out of process function is used. Unfortunately the solution mentioned by @JulesP96 does not seem to work in that scenario. I added it to the program.cs but if I set a breakpoint it hits only after the function is started.

.ConfigureServices((context, services) => { var configuration = context.Configuration; services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

davicbaba commented 2 years ago

Is there also a solution for this problem in case a out of process function is used. Unfortunately the solution mentioned by @JulesP96 does not seem to work in that scenario. I added it to the program.cs but if I set a breakpoint it hits only after the function is started.

.ConfigureServices((context, services) => { var configuration = context.Configuration; services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

I have the same problem, did you find any solution?

beukerz commented 2 years ago

No unfortunately not. I am using the functions configuration for now. The out of process functions are not mature at all, there are many things that do not work yet or only work with a work around if you do something more than the basic stuff.

zhenlan commented 1 year ago

The support for expression resolution for the out-of-process Azure Functions is tracked at https://github.com/Azure/azure-functions-dotnet-worker/issues/1253.

christianarg commented 1 year ago

Is there any update on this? Now NET7 only supports azure functions isolated and having to replicate configurations in each function instead of using app configuration is pretty bad. Considering we COULD do it in Azure Function In-Process

zhenlan commented 1 year ago

While we are waiting for the Azure Functions team to address https://github.com/Azure/azure-functions-dotnet-worker/issues/1253. An alternative solution is to leverage the App Configuration reference feature in App Service/Azure Functions:

Assume you have a Storage queue triggered Function app and you reference the queue name from the variable queuename in your Function. You can

  1. Add a key-value named Storage:QueueName with no label in App Configuration for the actual queue name.
  2. Create an App Setting queuename in Azure Functions and set its value to something like this:
    @Microsoft.AppConfiguration(Endpoint=https://<your-store-name>.azconfig.io; Key=Storage:QueueName)

What's used in step 2 is an App Configuration reference. See here for more information. This way, you can manage your Function trigger information in App Configuration and step 2 is a one-time change.