Part -2: Expert Guide for Azure Functions Triggers Tutorials
Introduction
Welcome back to another excellent blog that’s been personally composed by our DEV IT experts. The previous blog, “A Quick Guide to Azure Functions,” went over the descriptions of Azure Functions and covered topics including:
- What is Azure Function?
- What is serverless?
- Azure Function Example
Another blog on the site covered topics about “Using Constructor Injection to Implement DI in Microsoft Azure.” That’s another important blog when it comes to Azure Function Projects, and would be needed to ensure your code’s integrity while working on your Azure Function Project. Now, this new post will cover topics about Dependency Injection for Azure Functions V2 and V3. We won’t cover V1 for now, but if you want a blog on it as well, then please write a comment below, and I will write one soon.
So, let’s get right to it now. This blog will cover various triggers for the execution of Azure Functions.
What is an Azure Function Trigger?
An azure function trigger is a mechanism using which one can invoke the (manually or automatically) function. Consider trigger as an event; when the event occurs, it will invoke the function with appropriate data so that the Azure function can get enough context to decide upon.
There are different types of triggers azure function supports out of the box, which we will go through in this blog post. And if you find that your requirement is not getting covered with any of the existing out-of-the-box supported triggers, don’t worry. We will see an example of a custom trigger and see how a custom trigger can be implemented.
Azure Function Supported Triggers
Following triggers are generally used in any project:
- HTTP & Webhooks
- Timer
- Blog Storage
- Table Storage
- Queue Storage
- Service Bus
- Event Grid
- Event Hub
Http & Webhooks
HTTP & webhooks trigger is for invoking an azure function via making an HTTP call. I.e., You can build serverless APIs, or you can treat your APIs as webhook for third-party integration.
In the part – 1, we saw an example of the HTTPTrigger azure function by implementing a HelloWorld example. After the implementation, we saw that the function could be invoked on http://localhost:7071://api/helloworldfunction.
Few considerations you might want to take care of while implementing an HTTP Trigger function:
HTTP Connection Exhaustion
If you are calling HTTP API using HTTPClient in your HTTPTriger function, you might face a problem of port exhaustion which you can solve by sharing HTTPClient objects across multiple function executions. There are other ways as well which are explained here in more detail – Managing HTTP Connection in Azure Functions.
Securing HTTP Trigger function
By default, the HTTP trigger function allows anonymous connections. i.e., Anyone with a URL can make requests to our function. This can be solved in multiple ways:
1.Use Function Keys – Using this key, one can call only the respective azure function only.
2. Use Host Keys – Using this key, one can call all functions in an app service.
3. Use Master Keys – Using this key, one can get access to all functions as well as Azure function management APIs as well for an App Service.
4. Use App Service Authentication Mechanism – Azure app service (function or web app) has built-in support for authentication and authorization endpoints. One can use ASP.NET Identity underneath the azure function to manage identities and generate tokens and let app service authorization middleware handle the authorization part.
5. Use other Azure services such as APIM
6. Similar to using Authentication Mechanism, one can use API management capability to secure the APIs.
From the above-supported options, 4 and 5 are the recommended way to implement if you are going to expose the APIs over the internet in production.
Read here for more information about “Securing HTTP Trigger Functions.”
Important Note: If the azure trigger function fails, azure runtime won’t retry execution of the function, i.e., the Retry mechanism is not built-in for the TimerTrigger function.
Timer
As the name suggests, the timer trigger allows one to invoke a function based on predefined time. Consider a timer triggered function as a schedule function that can execute every day at 12 PM, every Monday at 3:00 AM, or every 30 minutes on Mon, Wed., i.e., Depending on the schedule we define, the timer function will get executed by Azure function runtime.
The timer trigger gets executed based on the NCRONTab expression. For example “0 */5 * * * *” is a cron expression for “once every 5 minutes” and “0 30 9 * Jan Mon” is the cron expression for “at 9:30 AM every Monday in January”.
Note: CRON expression and times in Azure function (or all azure services) are based on UTC only.
Let’s see an example:
[FunctionName("ScheduledImport")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo timerRequest, ILogger logger)
{
log.LogInformation($"Timer is running at {DateTime.UtcNow}!");
}
From the example above, we can see that TimerTrigger takes a NCRON expression as input and decides when to execute the function. You can also get this expression from the local.development.json file. It sends TimerInfo as request which as properties such as:
- “ScheduleStatus.Last” – Last run time of azure function
- “ScheduleStatus.Next” – Next run time of azure function
- “IsPastDue” – whether the function is triggered later than the scheduled time.
Important Note: If the azure trigger function fails, azure runtime won’t retry execution of the function, i.e., the Retry mechanism is not built-in for the TimerTrigger function.
Blob Storage
Blog Storage triggered function will get executed when azure function runtime detects a new/updated blob in a configured container. Since detecting new/updated blobs can be challenging in terms of performance, azure runtime may ignore some events which can cause unexpected behaviors. i.e., the azure function may not get executed for blob events because those can be missed.
Let’s see an example:
[FunctionName("ProfilePictureModify")]
public static void Run([BlobTrigger("profile-pictures/{name}")] Stream blobRequest, string name, ILogger logger)
{
log.LogInformation($"Function is executing for blob name:{name} and Size: {myBlob.Length} Bytes");
}
Important Notes:
- When a blob trigger function fails, it automatically retries up to 5 times. If all five retries are failed, the reference of blob execution is added into queue storage as a poisoned blob. (i.e., the blob will remain as-is in the storage account)
- MaxDequeueCount is a setting that can be used to perform retries and is default set to 5.
- By default, 24 blob functions can trigger concurrently due to the default limit, which can be changed in the host.json file.
For more information on the above points, read here – “Concurrency and Memory usage.”
Table Storage
We will not look at table storage in more detail as the schematics are similar to blob storage with minor differences, which can be read from here – Table Storage Trigger.
Queue Storage
Queue storage triggered is amongst widely used azure function triggers which are quite identical to “ServiceBus,” “EventHub,” and “EventGrid.” So we will see examples of Queue Trigger and then cover key differences between others.
Queue triggered functions are invoked when a message is available in queue storage.
Let’s take an example:
[FunctionName("SendNotificationForGroupCreation")]
public static void Run(
[QueueTrigger("group creation notification queue", Connection = "StorageConnectionSetting")] string groupCreationRequest,
ILogger log)
{
log.LogInformation($"Function is executing request for {groupCreationRequest}");
}
From the above example, one can see that the function will be invoked/executed whenever a message is found in a queue named “group creation notification queue” in the storage account “StorageConnectionSetting.”
The incoming request can be a string (group creation request) or an object (in which case azure runtime will deserialize the string to a provided type).
Important Notes:
- If a function fails, azure runtime retries for five attempts, and if all attempts fail, then azure runtime will move the message to poison queue, i.e. {Original-Queue-Name}-poison.
- By default, queue batch size is 16, i.e., Azure runtime will get 16 messages from the queue and execute them in parallel. These settings can be changed in the host.json file.
The key difference between Queue Storage Trigger and Service Bus Trigger:
- While the queue storage triggered function can read messages from queue storage, the Service bus triggered function can read messages from queue and topics.
- Retry count for queue storage is defined and handled by the function, whereas for service bus, it is defined based on message delivery count (and also function level).
The below table can be considered as a reference for supported trigger types per the azure function version.
Type | 1.x | 2.x and higher | Trigger | Input | Output |
---|---|---|---|---|---|
Blob Storage | ✔ | ✔ | ✔ | ✔ | ✔ |
Azure Cosmos DB | ✔ | ✔ | ✔ | ✔ | ✔ |
Dapr3 | ✔ | ✔ | ✔ | ✔ | |
Event Grid | ✔ | ✔ | ✔ | ✔ | |
Event Hubs | ✔ | ✔ | ✔ | ✔ | |
HTTP & webhooks | ✔ | ✔ | ✔ | ✔ | |
IoT Hub | ✔ | ✔ | ✔ | ✔ | |
Kafka2 | ✔ | ✔ | ✔ | ||
Mobile Apps | ✔ | ✔ | ✔ | ||
Notification Hubs | ✔ | ✔ | |||
Queue storage | ✔ | ✔ | ✔ | ✔ | |
RabbitMQ2 | ✔ | ✔ | ✔ | ||
SendGrid | ✔ | ✔ | ✔ | ||
Service Bus | ✔ | ✔ | ✔ | ✔ | |
SignalR | ✔ | ✔ | ✔ | ||
Table storage | ✔ | ✔ | ✔ | ✔ | |
Timer | ✔ | ✔ | ✔ | ||
Twilio | ✔ | ✔ | ✔ |
Custom Azure Function Trigger
In the previous section, we saw different types of triggers that the Azure function supports. If none of those are not meeting your requirement, you can write down a custom trigger.
Primarily writing a custom extension involves two-part:
- Writing a Trigger – responsible for the invocation
- Writing a Binding – responsible for providing required data.
Let’s take a scenario of writing an azure function that executes based on the email received in one’s email box. (Assuming exchange or office 365 here)
Let’s first write down Trigger
[AttributeUsage(AttributeTargets.Parameter)]
[Binding]
public class OutlookTriggerAttribute: Attribute
{
public string GraphConnectionString { get; set; }
public string EmailId { get; set; }
public string Password { get; set; }
internal string GetOfficeGraphUri()
{
return Environment.GetEnvironmentVariable(GraphConnectionString );
}
}
Now let’s implement a Listener responsible for reading messages:
public class OutlookListener: IListener
{
private readonly ITriggeredFunctionExecutor _executor;
private readonly OutlookTriggerContext _context;
public OutlookListener(ITriggeredFunctionExecutor executor, OutlookTriggerContext context)
{
_executor = executor;
_context = context;
}
public void cancel()
{
if (_context == null || _context.Client == null) return;
_context.Client.Disconnect();
}
public void Dispose()
{
_context.Client.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
while (true)
{
wtoken.Token.ThrowIfCancellationRequested();
//_context.Client.GetMessages();
// … Get message from graph API an
var triggerData = new TriggeredFunctionData
{
TriggerValue = outlookMessagePayload
};
var task = _executor.TryExecuteAsync(triggerData, CancellationToken.None);
task.Wait();
Task.Wait(10000);
}
}, wtoken, TaskCreationOptions.LongRunning);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>{
_context.Client.Disconnect();
});
}
}
}
Let’s implement Outlook Trigger context.
public class OutlookTriggerContext
{
public OutlookTriggerAttribute TriggerAttribute;
public GraphApiClient Client;
public OutlookTriggerContext(OutlookTriggerAttribute attribute, GraphApiClient client)
{
this.TriggerAttribute = attribute;
this.Client = client;
}
}
Now we will implement Binding for our outlook trigger
[assembly: WebJobsStartup(typeof(OutlookBinding.Startup))]
namespace WebJobs.Extension.Outlook
{
public class OutlookBinding
{
public class Outlookup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.AddOutlookExtension();
}
}
}
}
We need to add AddOutlookExtension method to finish with
public static class OutlookWebJobsBuilderExtensions
{
public static IWebJobsBuilder AddNatsExtension(this IWebJobsBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.AddExtension<OutlookExtensionConfigProvider>();
builder.Services.AddSingleton<IOutlookServiceFactory, OutlookServiceFactory>();
return builder;
}
}
Now we can write down an azure function that can trigger based on the email you receive in the email box:
public static class OutlookTriggerFunction
{
[FunctionName("OutlookTriggerFunction")]
public static void Run(
[OutlookTrigger(GraphConnectionString = "mail.outlook.com", EmailId = "myemail@outlook.com", Password = "Password")] string outlookMessage,
ILogger log)
{
log.LogInformation($"Message Received From outlook mailbox is {outlookMessage}");
}
}
Conclusion
I hope this blog has helped you learn about V2 and V3 triggers for Azure functions. Again, if you would like a blog on V1 as well, please feel free to mention it in the comments below. Also, if you have any other doubts, please comment below, and our DEV IT engineers will get back to you.