How can I resolve Azure basic function app error - azure

I have created one basic function app in VS2017 and tried run, facing
issue below.
Microsoft.Azure.WebJobs.Host: Error indexing method 'Fun
ction1.Run'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'log' to type TraceWriter. Make sure the parameter Type is supported by the binding. If you're
using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config."
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
namespace FunctionAppLatest
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, 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;
if (name == null)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
name = data?.name;
}
return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
}
}

Functions 2.0 in Azure no longer use TraceWriter.
Please replace it by ILogger log
Instead of log.info you will need to use log.LogInformation("C# HTTP trigger function processed a request.");

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:

FromQuery support in Azure Functions v3

I am trying to use [FromQuery] with Azure Function v3 but I am getting the following error:
Cannot bind parameter 'search' to type String.
For the following method:
[FunctionName("GetObjects")]
public ActionResult<IActionResult> QueryObjects(
[HttpTrigger(AuthorizationLevel.Function, "GET", Route = "objects")]
HttpRequest req,
ILogger log,
[FromQuery] string search = null)
{
//do some stuff
}
Is [FromQuery] not supported?
Should I use req.Query["search"] to get the query parameter?
From functions.desp.json
Related to binding
"Microsoft.Extensions.Configuration.Binder/3.1.1": {
"dependencies": {
"Microsoft.Extensions.Configuration": "3.1.2"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": {
"assemblyVersion": "3.1.1.0",
"fileVersion": "3.100.119.61404"
}
}
},
This is what you face now:
Method signatures developed by the azure function C # class library can include these:
ILogger or TraceWriter for logging (v1 version only)
A CancellationToken parameter for graceful shutdown
Mark input and output bindings by using attribute decoration
Binding expressions parameters to get trigger metadata
From this doc, it seems that it is not supported. You can create your custom binding like this, and dont forget to register it in the startup.
If you want to bind it directly, it's not possible. So you could try to change your route like Function1/name={name}&research={research} then bind it to string parameter.
Below is my test code:
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route="Function1/name={name}&research={research}")] HttpRequest req,
String name,
String research,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation(research);
string responseMessage = $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}

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.

Getting next message in an Azure Service Bus Subscription with an Azure Function, with an HTTP Trigger

I want to create an Azure Function that will fulfill the following requirements:
Trigger upon an HTTP request
Looks at an Azure Service Bus Subscription and gets the next message based on a set of filters specified in the HTTP Request.
If you are using C# you can do something like this:
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.ServiceBus;
using Microsoft.Azure.ServiceBus.Core;
namespace HttpTriggerSBRead
{
public static class ReadSBOnHttpTrigger
{
const string ServiceBusConnectionString = "{service bus connection string}";
const string TopicName = "{name of your topic}";
const string SubscriptionName = "{name of your subscription}";
[FunctionName("ReadSBOnHttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string filter = req.Query["filter"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
filter = filter ?? data?.filter;
SubscriptionClient sqlFilterOnlySubscriptionClient = new SubscriptionClient(ServiceBusConnectionString,
TopicName, SubscriptionName);
await sqlFilterOnlySubscriptionClient.AddRuleAsync(new RuleDescription
{
Filter = new SqlFilter(filter),
Name = filter
});
await ReceiveMessagesAsync(SubscriptionName, log);
await sqlFilterOnlySubscriptionClient.RemoveRuleAsync(filter);
return filter != null
? (ActionResult)new OkObjectResult($"{filter}")
: new BadRequestObjectResult("Please pass a filter on the query string or in the request body");
}
static async Task ReceiveMessagesAsync(string subscriptionName, ILogger log)
{
string subscriptionPath = EntityNameHelper.FormatSubscriptionPath(TopicName, subscriptionName);
IMessageReceiver subscriptionReceiver = new MessageReceiver(ServiceBusConnectionString, subscriptionPath, ReceiveMode.ReceiveAndDelete);
log.LogInformation($"{DateTime.Now} :: Receiving Messages From Subscription: {subscriptionName}");
var receivedMessage = await subscriptionReceiver.ReceiveAsync(TimeSpan.FromSeconds(30));
if (receivedMessage != null)
{
log.LogInformation($"Lable Property = {receivedMessage.Label}");
}
log.LogInformation($"{DateTime.Now} :: Messages From Subscription: {subscriptionName}");
}
}
}
To use it you need to pass in the filter parameter to your function. For example something like this:
http://localhost:7071/api/ReadSBOnHttpTrigger?filter=sys.Label=%27test%27 or http://localhost:7071/api/ReadSBOnHttpTrigger?filter=sys.To=%27test%27
Just for the reference, I used this code as source with some small modifications:
https://github.com/Azure/azure-service-bus/tree/master/samples/DotNet/GettingStarted/Microsoft.Azure.ServiceBus/TopicSubscriptionWithRuleOperationsSample

Azure Event Grid - Event Delivery Security at Azure Function

I have been working on a POC related to Azure Event Grid integration with Azure Function. I am stuck on the Event delivery Security as mentioned here.
I am using Event Grid Trigger which is sent by built-in Event grid Subscription in Azure Blob Storage. I have added an access token as a query parameter in WebHook endpoint as mentioned in the above URL.
But I cannot access that parameter in the Function code. Can someone share a sample for doing this?
FYI - Below is the function definition in my code.
[FunctionName("EventGridFunc")]
public static void Run([EventGridTrigger]EventGridEvent eventGridEvent,
TraceWriter log)
{
log.Info("Received a trigger.");
log.Info(eventGridEvent.Data.ToString());
}
The full subscriberUrl for EventGridTrigger function has the following format:
https://{FunctionAppName}.azurewebsites.net/admin/extensions/EventGridExtensionConfig?functionName={EventGridTriggerFunctionName}&code={masterKey}
As you can see, the EventGridTrigger is basically a special HttpTrigger (push) function with a "hidden pre-processing" of the event message for its validation.
Update:
I didn't see a way how to obtain a query string in the EventGridTrigger. However, there are few workarounds for your solution such as:
using an Application settings
using an Azure Key Vault for storing a secret
using a HttpTrigger instead of EventGridTrigger
The following code snippet shows an example of the HttpTrigger function for EventGrid (version 2018-05-01-preview) subscriber:
#r "Newtonsoft.Json"
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Threading.Tasks;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, IDictionary<string, string> query, TraceWriter log)
{
log.Info("C# HTTP trigger function processed an EventGrid request.");
log.Info($"\nHeaders:\n\t{string.Join("\n\t", req.Headers.Where(i => i.Key.StartsWith("aeg-")).Select(i => $"{i.Key}={i.Value.First()}"))}");
log.Info($"\nQuery:\n\t{string.Join("\n\t", query.Select(i => $"{i.Key}={i.Value}"))}");
string eventGridValidationHeader = req.Headers.FirstOrDefault( x => string.Compare(x.Key,"Aeg-Event-Type", true) == 0).Value?.FirstOrDefault().Trim();
// media type = application/json or application/cloudevents+json
string jsontext = null;
var jtoken = JToken.Parse(await req.Content.ReadAsStringAsync());
log.Info($"\n{jtoken.ToString(Newtonsoft.Json.Formatting.Indented)}");
if (jtoken is JArray)
jsontext = jtoken.SingleOrDefault<JToken>().ToString();
else if (jtoken is JObject)
jsontext = jtoken.ToString();
var eventGridEvent = JsonConvert.DeserializeAnonymousType(jsontext, new { EventType = "", Data = new JObject()});
if (string.IsNullOrEmpty(eventGridValidationHeader) || string.IsNullOrEmpty(eventGridEvent?.EventType) || eventGridEvent?.Data == null)
{
return req.CreateErrorResponse(HttpStatusCode.BadRequest, "No EventGrid message.");
}
if (eventGridValidationHeader == "SubscriptionValidation" && eventGridEvent.EventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
{
log.Verbose(#"Event Grid Validation event received.");
return req.CreateResponse(HttpStatusCode.OK, JsonConvert.DeserializeObject(JsonConvert.SerializeObject(new { validationResponse = ((dynamic)eventGridEvent.Data).validationCode })));
}
#region Event Processing
// for testing a retry delivery policy
//return req.CreateResponse(HttpStatusCode.BadRequest, "Testing");
#endregion
return req.CreateResponse(HttpStatusCode.NoContent);
}

Resources