Azure Event Grid binding spec

Detailed documentation on the Azure Event Grid binding component

See this for Azure Event Grid documentation.

Setup Dapr component

  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 Input Binding Metadata
  10. - name: tenantId
  11. value: "[AzureTenantId]"
  12. - name: subscriptionId
  13. value: "[AzureSubscriptionId]"
  14. - name: clientId
  15. value: "[ClientId]"
  16. - name: clientSecret
  17. value: "[ClientSecret]"
  18. - name: subscriberEndpoint
  19. value: "[SubscriberEndpoint]"
  20. - name: handshakePort
  21. value: [HandshakePort]
  22. - name: scope
  23. value: "[Scope]"
  24. # Optional Input Binding Metadata
  25. - name: eventSubscriptionName
  26. value: "[EventSubscriptionName]"
  27. # Required Output Binding Metadata
  28. - name: accessKey
  29. value: "[AccessKey]"
  30. - name: topicEndpoint
  31. value: "[TopicEndpoint]

Warning

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

Input Binding Metadata

  • tenantId is the Azure tenant id in which this Event Grid Event Subscription should be created
  • subscriptionId is the Azure subscription id in which this Event Grid Event Subscription should be created
  • clientId is the client id that should be used by the binding to create or update the Event Grid Event Subscription
  • clientSecret is the client secret that should be used by the binding to create or update the Event Grid Event Subscription
  • subscriberEndpoint is the https (required) endpoint in which Event Grid will handshake and send Cloud Events. If you aren’t re-writing URLs on ingress, it should be in the form of: https://[YOUR HOSTNAME]/api/events If testing on your local machine, you can use something like ngrok to create a public endpoint.
  • handshakePort is the container port that the input binding will listen on for handshakes and events
  • scope is the identifier of the resource to which the event subscription needs to be created or updated. The scope can be a subscription, or a resource group, or 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.

  • eventSubscriptionName (Optional) is the name of the event subscription. Event subscription names must be between 3 and 64 characters in length and should use alphanumeric letters only.

Output Binding Metadata

  • accessKey is the Access Key to be used for publishing an Event Grid Event to a custom topic
  • topicEndpoint is the topic endpoint in which this output binding should publish events

Output Binding Supported Operations

  • create

Additional information

Event Grid Binding creates an event subscription when Dapr initializes. Your Service Principal needs to have the RBAC permissions to enable this.

  1. # First ensure that Azure Resource Manager provider is registered for Event Grid
  2. az provider register --namespace Microsoft.EventGrid
  3. az provider show --namespace Microsoft.EventGrid --query "registrationState"
  4. # Give the SP needed permissions so that it can create event subscriptions to Event Grid
  5. az role assignment create --assignee <clientId> --role "EventGrid EventSubscription Contributor" --scopes <scope>

Make sure to also to add quotes around the [HandshakePort] in your Event Grid binding component because Kubernetes expects string values from the config.

Testing locally

  • Install ngrok
  • Run locally using custom port 9000 for handshakes
  1. # Using random port 9000 as an example
  2. ngrok http -host-header=localhost 9000
  • Configure the ngrok’s HTTPS endpoint and 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 om Kubernetes

Azure Event Grid requires a valid HTTPS endpoint for custom webhooks. Self signed certificates won’t do. In order to enable traffic from 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 dapr-annotations.yaml 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 NGINX ingress controller to your Kubernetes cluster with Helm 3 using the annotations

  1. helm repo add stable https://kubernetes-charts.storage.googleapis.com/
  2. helm install nginx stable/nginx-ingress -f ./dapr-annotations.yaml -n default
  3. # Get the public IP for the ingress controller
  4. 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 MS 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 .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 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..

Related links

Last modified February 16, 2021: Merge pull request #1235 from dapr/update-v0.11 (b4e9fbb)