Azure Event Grid / Function / ngrok - azure

I'm trying to follow the instructions Local Testing with ngrok
I have my event gird running and my function running in VS locally using the C# example. However, when I try to subscribe to my event using the endpoint
https://xxxx.ngrok.io/admin/extensions/EventGridExtensionConfig?functionName=EventGridTrigger
My local ngrok console shows:
POST /admin/extensions/EventGridExtensionConfig 404 Not Found
Function code in VS:
[FunctionName("EventGridTrigger")]
public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, TraceWriter log)
{
log.Info(eventGridEvent.Data.ToString());
}

based on your description, the following attribute must be used:
[FunctionName("EventGridTrigger")]
you can test it with the Postman:
http://localhost:7071/admin/extensions/EventGridExtensionConfig?functionName=EventGridTrigger
note, that the following header must be added:
Aeg-Event-Type:Notification
Update:
the following is my working function via ngrok and custom topic created by VS 2017 Version 15.7.5:
// This is the default URL for triggering event grid function in the local environment.
// http://localhost:7071/admin/extensions/EventGridExtensionConfig?functionName={functionname}
// Aeg-Event-Type:Notification
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace FunctionApp10
{
public static class Function2
{
[FunctionName("Function2")]
public static void Run([EventGridTrigger]JObject eventGridEvent, TraceWriter log)
{
log.Info(eventGridEvent.ToString(Formatting.Indented));
}
}
}
and the dependencies:
Update2:
the function for version 2 generated by VS from the EventGridTrigger template is the following:
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGridExtensionConfig?functionName={functionname}
// Aeg-Event-Type:Notification
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.EventGrid.Models;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Extensions.Logging;
namespace FunctionApp11
{
public static class Function2
{
[FunctionName("Function2")]
public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
{
log.LogInformation(eventGridEvent.Data.ToString());
}
}
}
and the dependencies:
Note for localhost:7071 postman test:
the payload must be as an array of the events

Apparently there is an error in the documentation published by Microsoft. The ngrok example is for functions v1. If you are using functions v2 then this is the URL required to trigger the function:
https://{subdomain}.ngrok.io/runtime/webhooks/EventGridExtensionConfig?functionName={functionName}
See the issue logged here

For function 2.x the url is https://{subdomain}.ngrok.io/runtime/webhooks/eventgrid?functionName={functionName}.
See the docs.

Related

Azure Function binding working locally but not in portal

I have this azure function v3:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Cosmos.Table;
namespace FunctionApp4
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[Table("Items")] CloudTable table,
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;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
Runs perfectly locally but when published to the portal I get:
Error indexing method 'Function1' Cannot bind parameter 'table' to type CloudTable. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
Does anyone know what could be causing this?
These are the dependencies for the function:

Adding Custom Dimension to Request Telemetry - Azure functions

I am creating a new Function app using v2.x and I am integrating Application Insights for request logging that is automatically being done as Azure Function is now integrated with App Insights (as mentioned in the documentation link). What I would need to do is log few custom fields in the custom dimensions in Application Insights Request Telemetry. Is it possible without using Custom Request logging (using TrackRequest method)
About adding custom properties, you could refer to this tutorial:Add properties: ITelemetryInitializer. The below is my test a HTTP trigger function.
public static class Function1
{
private static string key = "Your InstrumentationKey";
private static TelemetryClient telemetry = new TelemetryClient() { InstrumentationKey = key };
[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.");
if (!telemetry.Context.Properties.ContainsKey("Function_appName"))
{
telemetry.Context.Properties.Add("Function_appName", "testfunc");
}
else
{
telemetry.Context.Properties["Function_appName"] = "testfunc";
}
telemetry.TrackEvent("eventtest");
telemetry.TrackTrace("tracetest");
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");
}
}
After running this function, go to the Application Insights Search could check the data Or go to Logs(Analytics).
Update:
You should use ITelemetry Initializer(which can add custom dimension to a specified telemetry like only for request) in function app, please follow the steps below:
1.In Visual studio, create a function app(In my test, I create a blob triggerd function), and install the following nuget packages:
Microsoft.ApplicationInsights, version 2.10.0
Microsoft.NET.Sdk.Functions, version 1.0.29
2.Then in the Function1.cs, write code like below:
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
[assembly: WebJobsStartup(typeof(FunctionApp21.MyStartup))]
namespace FunctionApp21
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([BlobTrigger("samples-workitems/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
}
}
internal class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
//use telemetry is RequestTelemetry to make sure only add to request
if (telemetry != null && telemetry is RequestTelemetry && !telemetry.Context.GlobalProperties.ContainsKey("my_custom_dimen22"))
{
telemetry.Context.GlobalProperties.Add("my_custom_dimen22", "Hello, this is custom dimension for request!!!");
}
}
}
public class MyStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
}
}
}
3.Publish it to azure, then nav to azure portal -> the published function app -> Monitor -> Add an application insights.
4.Run the function from azure. And wait for a few minutes -> nav to the application insights portal, check the telemetry data, and you can see the custom dimension is only added to request telemetry:
The other solutions don't quite answer the question, how to add custom properties to the request telemetry. There is a very simple solution, add the following within your function's code:
Activity.Current?.AddTag("my_prop", "my_value");
You'll need:
using System.Diagnostics;
This then can be dynamic per function invocation / request, rather a fixed global property.

Azure Function with EventGridTrigger prevents FunctionsStartup class's Configure method from running

I'm trying to have a function that has its dependencies injected as outlined in documentation, Use dependency injection in .NET Azure Functions. My Startup class is defined as:
using System.Runtime.CompilerServices;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyFunctions.Startup))]
namespace MyFunctions{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
}
}
}
I set a breakpoint on the builder.Services.AddHttpClient() statement to ensure the DI is configured.
Then I define my function using a HttpTrigger:
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
namespace MyFunctions
{
public class ChangeProducer
{
private readonly HttpClient _httpClient;
public ChangeProducer(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient();
}
[FunctionName("ChangeProducer")]
public void Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "Reservation")]HttpRequest request, ILogger log)
{
Console.WriteLine("foo");
}
}
}
When I run this from Visual Studio I hit the breakpoint in Startup.Configure. Wonderful!
Then I change my function to use an EventGridTrigger:
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
namespace MyFunctions
{
public class ChangeProducer
{
private readonly HttpClient _httpClient;
public ChangeProducer(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient();
}
[FunctionName("ChangeProducer")]
public void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
{
Console.WriteLine("foo");
}
}
}
Once that change is made, and no other change, I don't hit the breakpoint in Startup.Configure. Additionally, I know the DI is failing because when I try to invoke the function I get an error message that reads:
Executed 'ChangeProducer' (Failed, Id=06ae8b88-07c4-4150-91e5-8b88400aed72)
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'System.Net.Http.IHttpClientFactory' while attempting to activate 'MyFunctions.ChangeProducer'.
Is there a known issue? I can't figure this out. The only difference is the trigger type.
Update 2019-06-24 - It's just the dependency injection
I want to be clear the issue is dependency injection isn't working, there isn't an issue with the EventGridTrigger when the dependency on HttpClient is not injected. Change the constructor to the following and the function works fine when triggered by the EventGridTrigger:
public ChangeProducer()
{
_httpClient = new HttpClient();
}
I have successfully tested with the following packages and Visual Studio 2019, version 16.1.3.
The following screen snippets show the debugging steps:
On the Start-up
Invoking a function by POST
http://localhost:7071/runtime/webhooks/EventGrid?functionName=Function1
I've figured out the dependency injection issue after creating a new function app and slowly building it out and comparing to the project that was experiencing the problem. The host.json file contained a property named extensionBundle. Once I removed that the dependency injection started working again. The entire property looks like this:
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}

Locally testing Azure Functions with DocumentDB (client library) and receiving 'Invalid API version' for 1.13.2 to 1.17.0

I'm testing Azure Functions locally using VS2017 (Preview 7.1). The function writes to DocumentDB locally using the emulator (1.11.136.2) and everything works fine when using Microsoft.Azure.DocumentDB 1.13.1. As soon as I upgrade to any of the newer versions (1.13.2 to 1.17.0), I receive the following error:
Invalid API version. Ensure a valid x-ms-version header value is passed.
When calling the function from Postman I add a x-ms-version: 2017-02-22 header, but I suspect this is required only for the REST API.
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using System;
using System.Configuration;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace AzureFunction
{
public static class Function1
{
private static readonly ConnectionPolicy connectionPolicy =
new ConnectionPolicy
{
UserAgentSuffix = " tilt",
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp,
EnableEndpointDiscovery = false,
};
[FunctionName("FunctionApp")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "func/app")]HttpRequestMessage req, TraceWriter log)
{
string setting = ConfigurationManager.AppSettings["DOCUMENTDB"];
string databaseName = "test1";
Tuple<Uri, string> conn = Connection(setting);
using (var client = new DocumentClient(conn.Item1, conn.Item2, connectionPolicy))
{
// error thrown at next line
await client.CreateDatabaseIfNotExistsAsync(new Database { Id = databaseName });
}
return req.CreateResponse(HttpStatusCode.OK);
}
static Tuple<Uri, string> Connection(string configSetting)
{
string[] setting = configSetting.Split(';');
string endpoint = setting[0].Split('=')[1];
string key = setting[1].Split('=')[1] + "==";
var t = new Tuple<Uri, string>(new Uri(endpoint), key);
return t;
}
}
}
I could continue using 1.13.1, but I would like to start using Graph DB which is not compatible with this version.
Why am I receiving this error for the client library, and why only from version 1.13.2?
It turns out the emulator was the incorrect version. For Microsoft.Azure.DocumentDB 1.14.0 and above, Azure DocumentDB Emulator 1.13.58.2 is required, which I installed from here https://chocolatey.org/packages/azure-documentdb-emulator.
Seems this site https://learn.microsoft.com/en-us/azure/cosmos-db/local-emulator points to the old version.

Azure Functions and DocumentDB triggers

Is it possible to specify the DocumentDB is to fire triggers when writing to DocumentDB?
I have an Azure function that pulls JSON messages off a Service Bus Queue and puts them into DocumentDB like so:
using System;
using System.Threading.Tasks;
public static string Run(string myQueueItem, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
return myQueueItem;
}
This inserts new documents into the database as they are added to the service bus queue, however I need DocumentDB to process these as they are added and add attachments. This cannot be done in the present setup and I would like to tell DocumentDB to fire a trigger.
I have tried something like this:
using System;
using System.Threading.Tasks;
public static string Run(string myQueueItem, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
return "x-ms-documentdb-post-trigger-include: addDocument\n" + myQueueItem;
}
It doesn't work and gives me errors like this:
Exception while executing function:
Functions.ServiceBusQueueTriggerCSharp1. Microsoft.Azure.WebJobs.Host:
Error while handling parameter _return after function returned:.
Newtonsoft.Json: Unexpected character encountered while parsing value:
x. Path '', line 0, position 0.
I like this setup because I can saturate the queue with requests to add records and they just buffer until the database can deal with it, which deals with spikes in demand, but it allows data offload from the client machine as fast as the network can carry it and then the queue/database combination gets caught up when demand drops again.
You could refer to the following code sample to create document with the trigger enabled in Azure Functions.
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
public static void Run(string myQueueItem, TraceWriter log)
{
string EndpointUri = "https://{documentdb account name}.documents.azure.com:443/";
string PrimaryKey = "{PrimaryKey}";
DocumentClient client = new DocumentClient(new Uri(EndpointUri), PrimaryKey);
client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri("{databaseid}", "{collenctionid}"), new MyChunk { MyProperty = "hello" },
new RequestOptions
{
PreTriggerInclude = new List<string> { "YourTriggerName" },
}).Wait();
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
}
public class MyChunk
{
public string MyProperty { get; set; }
}
Note: for using Microsoft.Azure.DocumentDB NuGet package in a C# function, please upload a project.json file to the function's folder in the function app's file system.
project.json
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.Azure.DocumentDB": "1.13.1"
}
}
}
}
Besides, please make sure you have created triggers in your DocumentDB, for details about creating triggers, please refer to this article.

Resources