Azure Event Grid binding spec

Detailed documentation on the Azure Event Grid binding component

Component format

To setup an Azure Event Grid binding create a component of type bindings.azure.eventgrid. See this guide on how to create and apply a binding configuration.

See this for the documentation for Azure Event Grid.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: <name>
  5. spec:
  6. type: bindings.azure.eventgrid
  7. version: v1
  8. metadata:
  9. # Required Output Binding Metadata
  10. - name: accessKey
  11. value: "[AccessKey]"
  12. - name: topicEndpoint
  13. value: "[TopicEndpoint]"
  14. # Required Input Binding Metadata
  15. - name: azureTenantId
  16. value: "[AzureTenantId]"
  17. - name: azureSubscriptionId
  18. value: "[AzureSubscriptionId]"
  19. - name: azureClientId
  20. value: "[ClientId]"
  21. - name: azureClientSecret
  22. value: "[ClientSecret]"
  23. - name: subscriberEndpoint
  24. value: "[SubscriberEndpoint]"
  25. - name: handshakePort
  26. # Make sure to pass this as a string, with quotes around the value
  27. value: "[HandshakePort]"
  28. - name: scope
  29. value: "[Scope]"
  30. # Optional Input Binding Metadata
  31. - name: eventSubscriptionName
  32. value: "[EventSubscriptionName]"
  33. # Optional metadata
  34. - name: direction
  35. value: "input, output"

Warning

The above example uses secrets as plain strings. It is recommended to use a secret store for the secrets as described here.

Spec metadata fields

FieldRequiredBinding supportDetailsExample
accessKeyYOutputThe Access Key to be used for publishing an Event Grid Event to a custom topic“accessKey”
topicEndpointYOutputThe topic endpoint in which this output binding should publish events“topic-endpoint”
azureTenantIdYInputThe Azure tenant ID of the Event Grid resource“tenentID”
azureSubscriptionIdYInputThe Azure subscription ID of the Event Grid resource“subscriptionId”
azureClientIdYInputThe client ID that should be used by the binding to create or update the Event Grid Event Subscription and to authenticate incoming messages“clientId”
azureClientSecretYInputThe client id that should be used by the binding to create or update the Event Grid Event Subscription and to authenticate incoming messages“clientSecret”
subscriberEndpointYInputThe HTTPS endpoint of the webhook Event Grid sends events (formatted as Cloud Events) to. If you’re not re-writing URLs on ingress, it should be in the form of: https://[YOUR HOSTNAME]/<path>”
If testing on your local machine, you can use something like ngrok to create a public endpoint.
https://[YOUR HOSTNAME]/<path>”
handshakePortYInputThe container port that the input binding listens on when receiving events on the webhook“9000”
scopeYInputThe identifier of the resource to which the event subscription needs to be created or updated. See the scope section for more details“/subscriptions/{subscriptionId}/“
eventSubscriptionNameNInputThe name of the event subscription. Event subscription names must be between 3 and 64 characters long and should use alphanumeric letters only“name”
directionNInput/OutputThe direction of the binding“input”, “output”, “input, output”

Scope

Scope is the identifier of the resource to which the event subscription needs to be created or updated. The scope can be a subscription, a resource group, a top-level resource belonging to a resource provider namespace, or an Event Grid topic. For example:

  • /subscriptions/{subscriptionId}/ for a subscription
  • /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName} for a resource group
  • /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName} for a resource
  • /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventGrid/topics/{topicName} for an Event Grid topic

Values in braces {} should be replaced with actual values.

Binding support

This component supports both input and output binding interfaces.

This component supports output binding with the following operations:

  • create: publishes a message on the Event Grid topic

Azure AD credentials

The Azure Event Grid binding requires an Azure AD application and service principal for two reasons:

  • Creating an event subscription when Dapr is started (and updating it if the Dapr configuration changes)
  • Authenticating messages delivered by Event Hubs to your application.

Requirements:

For the first purpose, you will need to create an Azure Service Principal. After creating it, take note of the Azure AD application’s clientID (a UUID), and run the following script with the Azure CLI:

  1. # Set the client ID of the app you created
  2. CLIENT_ID="..."
  3. # Scope of the resource, usually in the format:
  4. # `/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventGrid/topics/{topicName}`
  5. SCOPE="..."
  6. # First ensure that Azure Resource Manager provider is registered for Event Grid
  7. az provider register --namespace "Microsoft.EventGrid"
  8. az provider show --namespace "Microsoft.EventGrid" --query "registrationState"
  9. # Give the SP needed permissions so that it can create event subscriptions to Event Grid
  10. az role assignment create --assignee "$CLIENT_ID" --role "EventGrid EventSubscription Contributor" --scopes "$SCOPE"

For the second purpose, first download a script:

  1. curl -LO "https://raw.githubusercontent.com/dapr/components-contrib/master/.github/infrastructure/conformance/azure/setup-eventgrid-sp.ps1"

Then, using PowerShell (pwsh), run:

  1. # Set the client ID of the app you created
  2. $clientId = "..."
  3. # Authenticate with the Microsoft Graph
  4. # You may need to add the -TenantId flag to the next command if needed
  5. Connect-MgGraph -Scopes "Application.Read.All","Application.ReadWrite.All"
  6. ./setup-eventgrid-sp.ps1 $clientId

Note: if your directory does not have a Service Principal for the application “Microsoft.EventGrid”, you may need to run the command Connect-MgGraph and sign in as an admin for the Azure AD tenant (this is related to permissions on the Azure AD directory, and not the Azure subscription). Otherwise, please ask your tenant’s admin to sign in and run this PowerShell command: New-MgServicePrincipal -AppId "4962773b-9cdb-44cf-a8bf-237846a00ab7" (the UUID is a constant)

Testing locally

  • Install ngrok
  • Run locally using a custom port, for example 9000, for handshakes
  1. # Using port 9000 as an example
  2. ngrok http --host-header=localhost 9000
  • Configure the ngrok’s HTTPS endpoint and the custom port to input binding metadata
  • Run Dapr
  1. # Using default ports for .NET core web api and Dapr as an example
  2. dapr run --app-id dotnetwebapi --app-port 5000 --dapr-http-port 3500 dotnet run

Testing on Kubernetes

Azure Event Grid requires a valid HTTPS endpoint for custom webhooks; self-signed certificates aren’t accepted. In order to enable traffic from the public internet to your app’s Dapr sidecar you need an ingress controller enabled with Dapr. There’s a good article on this topic: Kubernetes NGINX ingress controller with Dapr.

To get started, first create a dapr-annotations.yaml file for Dapr annotations:

  1. controller:
  2. podAnnotations:
  3. dapr.io/enabled: "true"
  4. dapr.io/app-id: "nginx-ingress"
  5. dapr.io/app-port: "80"

Then install the NGINX ingress controller to your Kubernetes cluster with Helm 3 using the annotations:

  1. helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
  2. helm repo update
  3. helm install nginx-ingress ingress-nginx/ingress-nginx -f ./dapr-annotations.yaml -n default
  4. # Get the public IP for the ingress controller
  5. kubectl get svc -l component=controller -o jsonpath='Public IP is: {.items[0].status.loadBalancer.ingress[0].ip}{"\n"}'

If deploying to Azure Kubernetes Service, you can follow the official Microsoft documentation for rest of the steps:

  • Add an A record to your DNS zone
  • Install cert-manager
  • Create a CA cluster issuer

Final step for enabling communication between Event Grid and Dapr is to define http and custom port to your app’s service and an ingress in Kubernetes. This example uses a .NET Core web api and Dapr default ports and custom port 9000 for handshakes.

  1. # dotnetwebapi.yaml
  2. kind: Service
  3. apiVersion: v1
  4. metadata:
  5. name: dotnetwebapi
  6. labels:
  7. app: dotnetwebapi
  8. spec:
  9. selector:
  10. app: dotnetwebapi
  11. ports:
  12. - name: webapi
  13. protocol: TCP
  14. port: 80
  15. targetPort: 80
  16. - name: dapr-eventgrid
  17. protocol: TCP
  18. port: 9000
  19. targetPort: 9000
  20. type: ClusterIP
  21. ---
  22. apiVersion: extensions/v1beta1
  23. kind: Ingress
  24. metadata:
  25. name: eventgrid-input-rule
  26. annotations:
  27. kubernetes.io/ingress.class: nginx
  28. cert-manager.io/cluster-issuer: letsencrypt
  29. spec:
  30. tls:
  31. - hosts:
  32. - dapr.<your custom domain>
  33. secretName: dapr-tls
  34. rules:
  35. - host: dapr.<your custom domain>
  36. http:
  37. paths:
  38. - path: /api/events
  39. backend:
  40. serviceName: dotnetwebapi
  41. servicePort: 9000
  42. ---
  43. apiVersion: apps/v1
  44. kind: Deployment
  45. metadata:
  46. name: dotnetwebapi
  47. labels:
  48. app: dotnetwebapi
  49. spec:
  50. replicas: 1
  51. selector:
  52. matchLabels:
  53. app: dotnetwebapi
  54. template:
  55. metadata:
  56. labels:
  57. app: dotnetwebapi
  58. annotations:
  59. dapr.io/enabled: "true"
  60. dapr.io/app-id: "dotnetwebapi"
  61. dapr.io/app-port: "5000"
  62. spec:
  63. containers:
  64. - name: webapi
  65. image: <your container image>
  66. ports:
  67. - containerPort: 5000
  68. imagePullPolicy: Always

Deploy the binding and app (including ingress) to Kubernetes

  1. # Deploy Dapr components
  2. kubectl apply -f eventgrid.yaml
  3. # Deploy your app and Nginx ingress
  4. kubectl apply -f dotnetwebapi.yaml

Note: This manifest deploys everything to Kubernetes’ default namespace.

Troubleshooting possible issues with Nginx controller

After initial deployment the “Daprized” Nginx controller can malfunction. To check logs and fix issue (if it exists) follow these steps.

  1. $ kubectl get pods -l app=nginx-ingress
  2. NAME READY STATUS RESTARTS AGE
  3. nginx-nginx-ingress-controller-649df94867-fp6mg 2/2 Running 0 51m
  4. nginx-nginx-ingress-default-backend-6d96c457f6-4nbj5 1/1 Running 0 55m
  5. $ kubectl logs nginx-nginx-ingress-controller-649df94867-fp6mg nginx-ingress-controller
  6. # If you see 503s logged from calls to webhook endpoint '/api/events' restart the pod
  7. # .."OPTIONS /api/events HTTP/1.1" 503..
  8. $ kubectl delete pod nginx-nginx-ingress-controller-649df94867-fp6mg
  9. # Check the logs again - it should start returning 200
  10. # .."OPTIONS /api/events HTTP/1.1" 200..

Last modified October 12, 2023: Update config.toml (#3826) (0ffc2e7)