Migrating in-proc Azure function to isolated function - How to use generic Binder - azure

I have a working .NET6 in-proc Azure Function. I'm in the process of converting it to an isolated worker process (out-of-proc) .net7 function.
I have a HttpTrigger function that should get a file from the blob-storage. Since the blob-storage is case-sensitive and all files are saved in upper-case, I need to make the input upper-case as well.
With the in-proc function, I did that by injecting Microsoft.Azure.WebJobs.IBinder binder and then use binder.BindAsync<byte[]>(new Microsoft.Azure.WebJobs.BlobAttribute($"%BlobContainerName%/{fooUpper}", FileAccess.Read)
This is the .net6 in-proc function:
[Function(nameof(GetFile))]
public async Task<IActionResult> GetFile(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "bar/{foo}")] HttpRequest req,
Microsoft.Azure.WebJobs.IBinder binder, Microsoft.Azure.WebJobs.ExecutionContext context, string foo)
{
// The file name is always upper-case:
var fooUpper = foo.ToUpperInvariant();
var blobStream = await binder.BindAsync<byte[]>(
new Microsoft.Azure.WebJobs.BlobAttribute($"%BlobContainerName%/{fooUpper}", FileAccess.Read),
req.FunctionContext.CancellationToken).ConfigureAwait(false);
return await ExecuteAsync(fooUpper, context.FunctionDirectory, blobStream,
req.FunctionContext.CancellationToken)
.ConfigureAwait(false);
}
Of course, I tried migrating this function to a .net7 isolated function by replacing HttpRequest req with HttpRequestData req and it compiles but when I run it binder is always null.
What is the isolated function version of my function?
Do I need to create a custom attribute?
Or create some middleware that makes the URL upper-case?
Or should I create a service that I register during start-up and use with DI in the function class?
Please advice.

Check if my below findings helpful to fix your issue:
BlobContainerClient is no longer working in isolated mode
AFAIK, BlobContainerClient should work with the .NET Isolated Process Version 7 in Azure Functions using Azure.Storage.Blobs NuGet Package as shown in this SO Answer #75015570 by the user #HariKrishna and MS DOC.
If you use the NuGet Package Azure.Storage.Blobs, you can initialize the BlobContainerClient class-objects which is compatible in .NET Isolated Process and for working with the Blob Container Objects, you have to initialize the BlobServiceClient for Connecting to Storage Account and getting access to the Blob Container.
public HttpResponseData Run([Microsoft.Azure.Functions.Worker.HttpTrigger(AuthorizationLevel.Function, "get", Route = "bar/{foo}")] HttpRequestData req,
BlobContainerClient blobContainerClient, IBinder binder, ExecutionContext executionContext)
Here is the GitHub Article on Azure Functions .NET Isolated Process of using the Ibinder input parameter for data binding and there are some examples provided in the same article for working in the context of Ibinder and StorageAccountAttribute such as Microsoft.Azure.WebJobs.BlobAttribute.

Related

Azure Functions v3 Could not load file or assembly 'Microsoft.Extensions.Primitives, Version=5.0.0.0

I'm having the above error after running an azure function called "Test" that redirects to an external URL of a service we want to use.
[FunctionName("Test")]
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequest req)
{
Log.Information("C# HTTP trigger function processed a request.");
string url = _authenticationService.GetAuthorizationUri().ToString();
return new RedirectResult(url);
}
The site at the URL prompts the user to authorize use of their data and performs a redirect to the previously authorized url of our "AuthorizationCallback", along with a query string parameter.
[FunctionName("AuthorizationCallback")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req)
{
Log.Information("C# HTTP trigger function processed a request.");
string code = req.Query["code"];
try
{
if (!string.IsNullOrEmpty(code))
{
await _authenticationService.ExchangeCodeForAccessToken(code);
return new OkResult();
}
}
catch (System.Exception)
{
return new UnauthorizedResult();
}
return new NotFoundResult();
}
The AuthorizationCallback function is hit but produces the following error in the console:
These are the dependencies of the current project on the solution (which is set as the startup project):
I've tried installing both the latest stable version (5.0.0) and the version before that (3.1.13) of Microsoft.Extensions.Primitives in the current project, but I'm still getting the same error. I've noticed the package that can't be loaded is within microsoft.azure.webjobs (3.0.23), which is within microsoft.azure.webjobs.extensions.storage (4.0.4), but these are used in another project entirely, for another azure function (blob triggered). Any ideas on how to overcome this error? Thank you all.
The Azure Functions host for .NET Core 3 uses an in-process hosting model, which essentially means you are limited in what versions of Microsoft assemblies you can use. What's happening is that something in your project has a reference to a newer version of Microsoft.Extensions.Primitives, but an older version of that library is already loaded by the Azure Functions host application.
For Azure Functions .NET Core 3, you should restrict all Microsoft.Extensions.* libraries to v3.x. You currently have Microsoft.Extensions.DependencyInjection 5.0.1, which should be changed to 3.x. Check for any other Microsoft.Extensions.* libraries either at the Packages level or anywhere beneath (tip: you can find them quickly by putting Microsoft.Extensions in the input box at the top of the Solution Explorer). You may need to downgrade some other library that has Microsoft.Extensions.Primitives as a dependency.
You might also be able to get away with manually writing a bindingRedirect pointing the newer version to an older version. The Microsoft.Extensions.* packages are relatively stable across versions, so that may work. It would make me very nervous, though.

Usage of Azure App Configuration's Feature Flags in Azure Functions

I'm working on exploring the following 2 features of Azure App Configuration in Azure Function's Http Trigger
Externalizing the App Settings
Feature Flags
Below is how i'm getting the reference of the configuration
So, when I use _configuration["SomeAppSettingKey"], I'm able to retrieve the value. So, I'm able to achieve #1 feature mentioned above.
My Question is, How do we retrieve the Feature Flag information? I have tried the below ways.
I would appreciate if someone could help me in understanding how to retrieve it in Azure Functions (I'm using V3)? A Sample code or any reference to documentation would be helpful.
Thanks.
Update1:
I can deserialize the json content as shown below. But, is this is the right approach?
Where FeatureManager is a class that I have defined as shown below.
all you need is to call UseFeatureFlags() function as part of AddAzureAppConfiguration to let the App Configuration provider know you want to use feature flags. An example can be found following the link below. It uses the FunctionsStartup and dependency injection (DI) of Azure Functions. An instance of a feature manager is put into the DI.
https://github.com/Azure/AppConfiguration/blob/master/examples/DotNetCore/AzureFunction/FunctionApp/Startup.cs
The link below shows how you can obtain the instance of IFeatureManagerSnapshot from DI and use it as part of your Azure Functions call.
https://github.com/Azure/AppConfiguration/blob/master/examples/DotNetCore/AzureFunction/FunctionApp/ShowBetaFeature.cs
Deserialize JSON is not a good idea, every time you will add new key you need to modify your class.
private static IConfiguration Configuration { set; get; }
static Function1()
{
var builder = new ConfigurationBuilder();
builder.AddAzureAppConfiguration(Environment.GetEnvironmentVariable("ConnectionString"));
Configuration = builder.Build();
}
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string keyName = "TestApp:Settings:Message";
string message = Configuration[keyName];
return message != null
? (ActionResult)new OkObjectResult(message)
: new BadRequestObjectResult($"Please create a key-value with the key '{keyName}' in App Configuration.");
}

Can I create non static Azure function class in C#, what are the consequences?

This non static class is required for constructor injection in Azure function and collection of custom telemetry events.
If we create an azure function app in visual studio, it creates default with static keyword like this:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
telemetryClient.TrackEvent(new Exception("Function started"));
}
But to use constructor dependency injection (for Temeltry client, i am using it), we need to remove static keyword.
public Function1(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
Previously, Azure Functions only supported static classes/methods. This restriction made DI via constructor impossible. However later the support for non-static classes/methods was implemented (see Support Instance Functions).
So if you need to use DI via constructor, just change it to non-static. There are no consequences.
This is not entirely true though - I just ran into some trouble with non static timer triggered functions. In my case I needed dependency injection in terms of entity framework, but this non static instance is now causing me trouble in order to call the admin endpoint to trigger the function when doing development locally.
See more on how to normally invoke static timer triggered functions here:
What is the simplest way to run a timer-triggered Azure Function locally once?

How do I post a message to a Azure Service Bus from my Azure function?

I am playing around with Azure functions and trying to setup an intricate construct of pipes and filters (the pattern). I am using Azure functions for this.
My starting point is a HTTP triggered Azure function. That collects some JSON from the body of the request and should pass this message to a Azure Service Bus.
I must be missing something here (I have done this before in RabbitMQ with console apps) but how do I post a message to a service bus from a HTTP triggered Azure function.
I found several references talking about configuring my function.json (what ever that is - im building in Visual Studio).
How does this input/output (trigger/binding) thing work - im guessing that is the problem here...or??
I have my first Azure function in place and is receiving the data from the HTTP - so far so good. But how to proceed?
** Update **
Still cant get it to Work. Got this code and it fails with an http500. (it also says i need to see the og - where is that log?)
public static class EI_WooCommerce_Hub
{
[FunctionName("EI_WooCommerce_Hub")]
[return: ServiceBus("eilogging", Connection = "EIIntegrationServiceBusConnection")]
public async static Task<string> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "EIWooCommerceHub/name/")]HttpRequestMessage req, TraceWriter log)
{
log.Info("Webhook triggered from: ");
return "Hello World to queue";
}
}
** Update 2 **
This seems to be a config problem. In the application settings on the function app I kept getting an authorization error for the servicebus. I added the default "AzureWebJobsServiceBus" setting with the connectionstring, then it worked. It could not pick up my own connection string for some reason.
You should use Service Bus output binding. Since you mentioned Visual Studio, I assume C#. The simplest ever example looks like this:
[FunctionName("ServiceBusOutput")]
[return: ServiceBus("myqueue", Connection = "ServiceBusConnection")]
public static string ServiceBusOutput([HttpTrigger] dynamic input)
{
return input.Text;
}
Then add an application setting called ServiceBusConnection with the connection string to a namespace with queue myqueue (or rename in attribute constructor).
You can find more in Azure Service Bus bindings for Azure Functions - Output.
When you build in Visual Studio, then the function.json is created automatically. All you have to do is to define your triggers and output as attributes of the function parameters (see here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus#output)
A minimalistic example:
[FunctionName("HttpTriggerCSharp")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestMessage req,
[ServiceBus("myqueue")] out string sbMessage,
TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
sbMessage = name;
}

local development azure functions 'HttpRequestMessage' does not contain a definition for 'GetQueryNameValuePairs'

I am having trouble running an azure function locally that works in the portal. I created a default C# Http trigger in Azure, then download the app content to run locally.
[5/22/18 9:03:21 PM] run.csx(8,23): error CS1061: 'HttpRequestMessage' does not contain a definition for 'GetQueryNameValuePairs' and no extension method 'GetQueryNameValuePairs' accepting a first argument of type 'HttpRequestMessage' could be found (are you missing a using directive or an assembly reference?)
[5/22/18 9:03:21 PM] run.csx(20,15): error CS1501: No overload for method 'CreateResponse' takes 2 arguments
The function works perfectly in the portal.
As #Mikhail has said, it's caused by the wrong functions core tools(CLI) version. The code you download works with v1 CLI(.Net Framework) while v2(you may have installed) is on .NET Core.
To install required CLI, follow CLI installation. If local OS(like MacOS) doesn't support v1 CLI we may turn to v2 CLI which can run cross-platform. With v2 CLI, v1 code created on portal can't be used, hence we need to create functions locally or change portal function app runtime to ~2 then create and download again.
Note that before we change the runtime, function app should be empty because functions created before depend on different runtime and usually will become invalid after the change.
To give a specific example of code that does work, the below would work in Functions v2:
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post",
Route = null)] HttpRequest req, ILogger log)
{
int id;
bool parsedId = int.TryParse(req.Query["id"], out id);
...
}

Resources