Can Azure function GridTrigger be used along with CosmosDB input binding? - azure

I am a newbie to Azure functions. I am having problem with the below code where CosmosDB binding is not working. If I remove CosmosDB, it works find; I am able to get messages thru eventGridEvent. But as soon as I add CosmosDB, this piece of code fails. I had tried different Collectors (IAsyncCollectory, IReadOnlyCollection, IReadOnlyList, IEnumerable, etc.) some of them compiles fine but when deployed to Azure portal it does nothing (msg: function not found).
I need to retrieve data from CosmosDb and save it to a Queue by using the message coming thru eventGridEvent. I can hardcoded but that is not allowed for my project. Any help will be appreciated team!
[FunctionName("TopicGridTrigger")]
public static void Run([EventGridTrigger()] EventGridEvent eventGridEvent,
[Queue("myqueuetest")] out string queueMessage,
[CosmosDB("MyCarStore", "cars", ConnectionStringSetting = "CosmosDBConnectionString")] IAsyncCollector<MyCar> Items,
ILogger log
)
{
//SOME CODE HERE
}

Related

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.");
}

Timer based Azure function with Table storage, HTTP request, and Azure Service Bus

I have a process written in a console application right now that fires on a scheduled task to read data from Azure table storage and based on that data, make API calls to a third party vendor we use, deserialize the response data, loop over an array in the results, save the individual iterations of the loop into a different table in Azure table storage, and then publish messages for each iteration of the loop to Azure service bus where those messages are consumed by another client.
In an effort to get more of our tasks into the cloud, I've done some research and it seems that an Azure function would be a good candidate to replace my console application. I spun up a new Azure function project in Visual Studio 2019 as a "timer" function and then dove into some reading where I got lost really fast.
The reading I've done talks about using "bindings" in my Run() method arguments decorated with attributes for connection strings etc but I'm not sure that is the direction I should be heading. It sounds like that would make it easier for authentication to my table storage, but I can't figure out how to use those "hooks" to query my table and then perform inserts. I haven't even gotten to the service bus stuff yet nor looked into making HTTP calls to our third party vendor's api.
I know this is a very broad question and I don't have any code to post because I'm having a tough time even getting out of the starting blocks with this. The MS documentation is all over the map and I can't find anything specific to my needs and I promise I've spent a fair bit of time trying.
Are Azure functions even the right path I should be travelling? If not, what other options are out there?
TIA
You should keep with Azure Functions with the Time Trigger to replace your console app.
The bindings (which can be used for input /output) are helpers to save you some lines of code, for example:
Rather than using the following code to insert data into azure table:
// Retrieve storage account information from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);
// Create a table client for interacting with the table service
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());
// Create a table client for interacting with the table service
CloudTable table = tableClient.GetTableReference("MyTable");
//some code to populate an entity
var entity = new { PartitionKey = "Http", RowKey = Guid.NewGuid().ToString(), Text = input.Text };
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
// Execute the operation.
TableResult result = await table.ExecuteAsync(insertOrMergeOperation);
you would use:
[FunctionName("TableOutput")]
[return: Table("MyTable")]
public static MyPoco TableOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# http trigger function processed: {input.Text}");
return new MyPoco { PartitionKey = "Http", RowKey = Guid.NewGuid().ToString(), Text = input.Text };
}
PS: the input trigger in the previous code is a HTTP Trigger, but was only to explain how to use output binding.
you can find more information in here:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-table
and you should watch: https://learn.microsoft.com/en-us/learn/modules/chain-azure-functions-data-using-bindings/

Function logging not showing details in Kudu - Log param always appears null

I am working through replacing some WebJobs with Azure Functions and appear to have all working OK except for logging of what is going on.
I have tried using the default function created within VS2017 and have run it locally with no problems. When deployed to Azure I cannot see the detail of the log entries anywhere.
Using Kudu to view the logs seemed OK except that I noticed that the log param of a function invocation is always null. This can't be the case as the function does run, and gives the expected output, and would fail if log was really null.
Here's what the function looks like:
public static class Function1
{
[FunctionName("Function1")]
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 name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
}
I would have expected to see the output of the log within Kudu by going to https://xxx.scm.azurewebsites.net/azurejobs/#/functions but I can't see the details when I toggle the output - it never loads (and it shows the log param as being null - perhaps is being resolved by DI though).
Whilst this function is simply a test I do have other more complicated functions that I need the logging details for.
I am not keen on investigating Application Insights as this seems way, way overkill for such simple functions that will not be used very heavily.
For v2 functions in runtime 2.x, doc explains
the WebJobs dashboard in the portal, which used the AzureWebJobsDashboard setting is replaced with Azure Application Insights, which uses the APPINSIGHTS_INSTRUMENTATIONKEY setting.
Like what you have seen, with AzureWebJobsDashboard setting, the log param is null and no log there. I am afraid the replaced with was enforced and Dashboard has been deprecated.
As for the usage of Application insights, we could just leverage the basic log monitoring without any further investigation. After creating it and add its instrumentationkey in Application settings, we could see the Monitor view is similar to Dashboard.
If we don't like it anyway, logs in kudu(https://<functionAppName>.scm.azurewebsites.net/DebugConsole) are ready there(D:\home\LogFiles\Application\Functions\function\<functoinName>).

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;
}

Azure WebJobs Connection Strings configuration ( AzureWebJobsDashboard?? )

I'm trying to work with Azure Webjobs, I understand the way its works but I don't understand why I need to use two connection strings, one is for the queue for holding the messages but
why there is another one called "AzureWebJobsDashboard" ?
What its purpose?
And where I get this connection string from ?
At the moment I have one Web App and one Webjob at the same solution, I'm experiment only locally ( without publishing anything ), the one thing I got up in the cloud is the Storage account that holds the queue.
I even try to put the same connection string in both places ( AzureWebJobsDashboard,AzureWebJobsStorage) but its throw exception :
"Cannot bind parameter 'log' when using this trigger."
Thank you.
There are two connection strings because the WebJobs SDK writes some logs in the storage account. It gives you the possibility of having one storage account just for data (AzureWebJobsStorage) and the another one for logs (AzureWebJobsDashboard). They can be the same. Also, you need two of them because you can have multiple job hosts using different data accounts but sending logs to the same dashboard.
The error you are getting is not related to the connection strings but to one of the functions in your code. One of them has a log parameter that is not of the right type. Can you share the code?
Okay, anyone coming here looking for an actual answer of "where do I get the ConnectionString from"... here you go.
On the new Azure portal, you should have a Storage Account resource; mine starts with "portalvhds" followed by a bunch of alphanumerics. Click that so see a resource Dashboard on the right, followed immediately by a Settings window. Look for the Keys submenu under General -- click that. The whole connection string is there (actually there are two, Primary and Secondary; I don't currently understand the difference, but let's go with Primary, shall we?).
Copy and paste that in your App.config file on the connectionString attribute of the AzureWebJobsDashboard and AzureWebJobsStorage items. This presumes for your environment you only have one Storage Account, and so you want that same storage to be used for data and logs.
I tried this, and at least the WebJob ran without throwing an error.
#RayHAz - Expanding upon your above answer (thanks)...
I tried this https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-get-started
but in .Net Core 2.1, was getting exceptions about how it couldn't find the connection string.
Long story short, I ended up with the following, which worked for me:
appsettings.json, in a .Net Core 2.1 Console app:
{
"ConnectionStrings": {
"AzureWebJobsStorage": "---your Azure storage connection string here---",
"AzureWebJobsDashboard":"---the same connectionstring---"
}
}
... and my Program.cs file...
using System;
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace YourWebJobConsoleAppProjectNamespaceHere
{
public class Program
{
public static IConfiguration Configuration;
static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json", true);
Configuration = builder.Build();
var azureWebJobsStorageConnectionString = Configuration.GetConnectionString("AzureWebJobsStorage");
var azureWebJobsDashboardConnectionString = Configuration.GetConnectionString("AzureWebJobsDashboard");
var config = new JobHostConfiguration
{
DashboardConnectionString = azureWebJobsDashboardConnectionString,
StorageConnectionString = azureWebJobsStorageConnectionString
};
var loggerFactory = new LoggerFactory();
config.LoggerFactory = loggerFactory.AddConsole();
var host = new JobHost(config);
host.RunAndBlock();
}
}
}

Resources