Azure Event Grid output binding for Azure Functions

Use the Event Grid output binding to write events to a custom topic. You must have a valid access key for the custom topic. The Event Grid output binding doesn't support shared access signature (SAS) tokens.

For information on setup and configuration details, see How to work with Event Grid triggers and bindings in Azure Functions.

Important

This article uses tabs to support multiple versions of the Node.js programming model. The v4 model is generally available and is designed to have a more flexible and intuitive experience for JavaScript and TypeScript developers. For more details about how the v4 model works, refer to the Azure Functions Node.js developer guide. To learn more about the differences between v3 and v4, refer to the migration guide.

Azure Functions supports two programming models for Python. The way that you define your bindings depends on your chosen programming model.

The Python v2 programming model lets you define bindings using decorators directly in your Python function code. For more information, see the Python developer guide.

This article supports both programming models.

Important

The Event Grid output binding is only available for Functions 2.x and higher.

Example

The type of the output parameter used with an Event Grid output binding depends on the Functions runtime version, the binding extension version, and the modality of the C# function. The C# function can be created using one of the following C# modes:

The following example shows how the custom type is used in both the trigger and an Event Grid output binding:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    public static class EventGridFunction
    {
        [Function(nameof(EventGridFunction))]
        [EventGridOutput(TopicEndpointUri = "MyEventGridTopicUriSetting", TopicKeySetting = "MyEventGridTopicKeySetting")]
        public static MyEventType Run([EventGridTrigger] MyEventType input, FunctionContext context)
        {
            var logger = context.GetLogger(nameof(EventGridFunction));
            logger.LogInformation(input.Data?.ToString());

            var outputEvent = new MyEventType()
            {
                Id = "unique-id",
                Subject = "abc-subject",
                Data = new Dictionary<string, object>
                {
                    { "myKey", "myValue" }
                }
            };

            return outputEvent;
        }
    }

    public class MyEventType
    {
        public string? Id { get; set; }

        public string? Topic { get; set; }

        public string? Subject { get; set; }

        public string? EventType { get; set; }

        public DateTime EventTime { get; set; }

        public IDictionary<string, object>? Data { get; set; }
    }
}

The following example shows a Java function that writes a message to an Event Grid custom topic. The function uses the binding's setValue method to output a string.

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<String> outputEvent,
            final ExecutionContext context) {
        context.getLogger().info("Java EventGrid trigger processed a request." + content);
        final String eventGridOutputDocument = "{\"id\": \"1807\", \"eventType\": \"recordInserted\", \"subject\": \"myapp/cars/java\", \"eventTime\":\"2017-08-10T21:03:07+00:00\", \"data\": {\"make\": \"Ducati\",\"model\": \"Monster\"}, \"dataVersion\": \"1.0\"}";
        outputEvent.setValue(eventGridOutputDocument);
    }
}

You can also use a POJO class to send Event Grid messages.

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<EventGridEvent> outputEvent,
            final ExecutionContext context) {
        context.getLogger().info("Java EventGrid trigger processed a request." + content);

        final EventGridEvent eventGridOutputDocument = new EventGridEvent();
        eventGridOutputDocument.setId("1807");
        eventGridOutputDocument.setEventType("recordInserted");
        eventGridOutputDocument.setEventTime("2017-08-10T21:03:07+00:00");
        eventGridOutputDocument.setDataVersion("1.0");
        eventGridOutputDocument.setSubject("myapp/cars/java");
        eventGridOutputDocument.setData("{\"make\": \"Ducati\",\"model\":\"monster\"");

        outputEvent.setValue(eventGridOutputDocument);
    }
}

class EventGridEvent {
    private String id;
    private String eventType;
    private String subject;
    private String eventTime;
    private String dataVersion;
    private String data;

    public String getId() {
        return id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getDataVersion() {
        return dataVersion;
    }

    public void setDataVersion(String dataVersion) {
        this.dataVersion = dataVersion;
    }

    public String getEventTime() {
        return eventTime;
    }

    public void setEventTime(String eventTime) {
        this.eventTime = eventTime;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getEventType() {
        return eventType;
    }

    public void setEventType(String eventType) {
        this.eventType = eventType;
    }

    public void setId(String id) {
        this.id = id;
    }  
}

The following example shows a timer triggered TypeScript function that outputs a single event:

import { app, EventGridPartialEvent, InvocationContext, output, Timer } from '@azure/functions';

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<EventGridPartialEvent> {
    const timeStamp = new Date().toISOString();
    return {
        id: 'message-id',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Henry',
        },
        eventTime: timeStamp,
    };
}

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.eventGrid({
        topicEndpointUri: 'MyEventGridTopicUriSetting',
        topicKeySetting: 'MyEventGridTopicKeySetting',
    }),
    handler: timerTrigger1,
});

To output multiple events, return an array instead of a single object. For example:

const timeStamp = new Date().toISOString();
return [
    {
        id: 'message-id',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Henry',
        },
        eventTime: timeStamp,
    },
    {
        id: 'message-id-2',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Doe',
        },
        eventTime: timeStamp,
    },
];

The following example shows a timer triggered JavaScript function that outputs a single event:

const { app, output } = require('@azure/functions');

const eventGridOutput = output.eventGrid({
    topicEndpointUri: 'MyEventGridTopicUriSetting',
    topicKeySetting: 'MyEventGridTopicKeySetting',
});

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: eventGridOutput,
    handler: (myTimer, context) => {
        const timeStamp = new Date().toISOString();
        return {
            id: 'message-id',
            subject: 'subject-name',
            dataVersion: '1.0',
            eventType: 'event-type',
            data: {
                name: 'John Henry',
            },
            eventTime: timeStamp,
        };
    },
});

To output multiple events, return an array instead of a single object. For example:

const timeStamp = new Date().toISOString();
return [
    {
        id: 'message-id',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Henry',
        },
        eventTime: timeStamp,
    },
    {
        id: 'message-id-2',
        subject: 'subject-name',
        dataVersion: '1.0',
        eventType: 'event-type',
        data: {
            name: 'John Doe',
        },
        eventTime: timeStamp,
    },
];

The following example demonstrates how to configure a function to output an Event Grid event message. The section where type is set to eventGrid configures the values needed to establish an Event Grid output binding.

{
  "bindings": [
    {
      "type": "eventGrid",
      "name": "outputEvent",
      "topicEndpointUri": "MyEventGridTopicUriSetting",
      "topicKeySetting": "MyEventGridTopicKeySetting",
      "direction": "out"
    },
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    }
  ]
}

In your function, use the Push-OutputBinding to send an event to a custom topic through the Event Grid output binding.

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$message = $Request.Query.Message

Push-OutputBinding -Name outputEvent -Value  @{
    id = "1"
    eventType = "testEvent"
    subject = "testapp/testPublish"
    eventTime = "2020-08-27T21:03:07+00:00"
    data = @{
        Message = $message
    }
    dataVersion = "1.0"
}

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = 200
    Body = "OK"
})

The following example shows a trigger binding and a Python function that uses the binding. It then sends in an event to the custom topic, as specified by the topicEndpointUri. The example depends on whether you use the v1 or v2 Python programming model.

Here's the function in the function_app.py file:

import logging
import azure.functions as func
import datetime

app = func.FunctionApp()

@app.function_name(name="eventgrid_output")
@app.event_grid_trigger(arg_name="eventGridEvent")
@app.event_grid_output(
    arg_name="outputEvent",
    topic_endpoint_uri="MyEventGridTopicUriSetting",
    topic_key_setting="MyEventGridTopicKeySetting")
def eventgrid_output(eventGridEvent: func.EventGridEvent, 
         outputEvent: func.Out[func.EventGridOutputEvent]) -> None:

    logging.log("eventGridEvent: ", eventGridEvent)

    outputEvent.set(
        func.EventGridOutputEvent(
            id="test-id",
            data={"tag1": "value1", "tag2": "value2"},
            subject="test-subject",
            event_type="test-event-1",
            event_time=datetime.datetime.utcnow(),
            data_version="1.0"))

Attributes

Both in-process and isolated worker process C# libraries use attribute to configure the binding. C# script instead uses a function.json configuration file as described in the C# scripting guide.

The attribute's constructor takes the name of an application setting that contains the name of the custom topic, and the name of an application setting that contains the topic key.

The following table explains the parameters for the EventGridOutputAttribute.

Parameter Description
TopicEndpointUri The name of an app setting that contains the URI for the custom topic, such as MyTopicEndpointUri.
TopicKeySetting The name of an app setting that contains an access key for the custom topic.
connection* The value of the common prefix for the setting that contains the topic endpoint URI. For more information about the naming format of this application setting, see Identity-based authentication.

Annotations

For Java classes, use the EventGridAttribute attribute.

The attribute's constructor takes the name of an app setting that contains the name of the custom topic, and the name of an app setting that contains the topic key. For more information about these settings, see Output - configuration. Here's an EventGridOutput attribute example:

public class Function {
    @FunctionName("EventGridTriggerTest")
    public void run(@EventGridTrigger(name = "event") String content,
            @EventGridOutput(name = "outputEvent", topicEndpointUri = "MyEventGridTopicUriSetting", topicKeySetting = "MyEventGridTopicKeySetting") OutputBinding<String> outputEvent, final ExecutionContext context) {
            ...
    }
}

Configuration

The following table explains the properties that you can set on the options object passed to the output.eventGrid() method.

Property Description
topicEndpointUri The name of an app setting that contains the URI for the custom topic, such as MyTopicEndpointUri.
topicKeySetting The name of an app setting that contains an access key for the custom topic.
connection* The value of the common prefix for the setting that contains the topic endpoint URI. When setting the connection property, the topicEndpointUri and topicKeySetting properties shouldn't be set. For more information about the naming format of this application setting, see Identity-based authentication.

Configuration

The following table explains the binding configuration properties that you set in the function.json file.

function.json property Description
type Must be set to eventGrid.
direction Must be set to out. This parameter is set automatically when you create the binding in the Azure portal.
name The variable name used in function code that represents the event.
topicEndpointUri The name of an app setting that contains the URI for the custom topic, such as MyTopicEndpointUri.
topicKeySetting The name of an app setting that contains an access key for the custom topic.
connection* The value of the common prefix for the setting that contains the topic endpoint URI. For more information about the naming format of this application setting, see Identity-based authentication.

*Support for identity-based connections requires version 3.3.x or higher of the extension.

When you're developing locally, add your application settings in the local.settings.json file in the Values collection.

Important

Make sure that you set the value of TopicEndpointUri to the name of an app setting that contains the URI of the custom topic. Don't specify the URI of the custom topic directly in this property. The same applies when using Connection.

See the Example section for complete examples.

Usage

The parameter type supported by the Event Grid output binding depends on the Functions runtime version, the extension package version, and the C# modality used.

When you want the function to write a single event, the Event Grid output binding can bind to the following types:

Type Description
string The event as a string.
byte[] The bytes of the event message.
JSON serializable types An object representing a JSON event. Functions tries to serialize a plain-old CLR object (POCO) type into JSON data.

When you want the function to write multiple events, the Event Grid output binding can bind to the following types:

Type Description
T[] where T is one of the single event types An array containing multiple events. Each entry represents one event.

For other output scenarios, create and use an EventGridPublisherClient with other types from Azure.Messaging.EventGrid directly. See Register Azure clients for an example of using dependency injection to create a client type from the Azure SDK.

Send individual messages by calling a method parameter such as out EventGridOutput paramName, and write multiple messages with ICollector<EventGridOutput>.

Access the output message by returning the value directly or using context.extraOutputs.set().

Access the output event by using the Push-OutputBinding cmdlet to send an event to the Event Grid output binding.

There are two options for outputting an Event Grid message from a function:

  • Return value: Set the name property in function.json to $return. With this configuration, the function's return value is persisted as an Event Grid message.
  • Imperative: Pass a value to the set method of the parameter declared as an Out type. The value passed to set is persisted as an Event Grid message.

Connections

There are two ways of authenticating to an Event Grid topic when using the Event Grid output binding:

Authentication method Description
Using a topic key Set the TopicEndpointUri and TopicKeySetting properties, as described in Use a topic key.
Using an identity Set the Connection property to the name of a shared prefix for multiple application settings, together defining identity-based authentication. This method is supported when using version 3.3.x or higher of the extension.

Use a topic key

Use the following steps to configure a topic key:

  1. Follow the steps in Get access keys to obtain the topic key for your Event Grid topic.

  2. In your application settings, create a setting that defines the topic key value. Use the name of this setting for the TopicKeySetting property of the binding.

  3. In your application settings, create a setting that defines the topic endpoint. Use the name of this setting for the TopicEndpointUri property of the binding.

Identity-based authentication

When using version 3.3.x or higher of the extension, you can connect to an Event Grid topic using an Microsoft Entra identity to avoid having to obtain and work with topic keys.

You need to create an application setting that returns the topic endpoint URI. The name of the setting should combine a unique common prefix (for example, myawesometopic) with the value __topicEndpointUri. Then, you must use that common prefix (in this case, myawesometopic) when you define the Connection property in the binding.

In this mode, the extension requires the following properties:

Property Environment variable template Description Example value
Topic Endpoint URI <CONNECTION_NAME_PREFIX>__topicEndpointUri The topic endpoint. https://<topic-name>.centralus-1.eventgrid.azure.net/api/events

More properties can be used to customize the connection. See Common properties for identity-based connections.

Note

When using Azure App Configuration or Key Vault to provide settings for managed identity-based connections, setting names should use a valid key separator such as : or / in place of the __ to ensure names are resolved correctly.

For example, <CONNECTION_NAME_PREFIX>:topicEndpointUri.

When hosted in the Azure Functions service, identity-based connections use a managed identity. The system-assigned identity is used by default, although a user-assigned identity can be specified with the credential and clientID properties. Note that configuring a user-assigned identity with a resource ID is not supported. When run in other contexts, such as local development, your developer identity is used instead, although this can be customized. See Local development with identity-based connections.

Grant permission to the identity

Whatever identity is being used must have permissions to perform the intended actions. For most Azure services, this means you need to assign a role in Azure RBAC, using either built-in or custom roles which provide those permissions.

Important

Some permissions might be exposed by the target service that are not necessary for all contexts. Where possible, adhere to the principle of least privilege, granting the identity only required privileges. For example, if the app only needs to be able to read from a data source, use a role that only has permission to read. It would be inappropriate to assign a role that also allows writing to that service, as this would be excessive permission for a read operation. Similarly, you would want to ensure the role assignment is scoped only over the resources that need to be read.

You must create a role assignment that provides access to your Event Grid topic at runtime. Management roles like Owner are not sufficient. The following table shows built-in roles that are recommended when using the Event Hubs extension in normal operation. Your application may require additional permissions based on the code you write.

Binding type Example built-in roles
Output binding EventGrid Contributor, EventGrid Data Sender

Next steps