Skip to content

Instantly share code, notes, and snippets.

@anuchandy
Created October 21, 2022 22:08
Show Gist options
  • Save anuchandy/546cdb33fa4b643b7715be4a00bb90bc to your computer and use it in GitHub Desktop.
Save anuchandy/546cdb33fa4b643b7715be4a00bb90bc to your computer and use it in GitHub Desktop.

Event Hubs, Service Principal (and optional CustomEndpointAddress)

Assuming you already have an Event Hubs namespace and an Event Hub in it, let's create a Service Principal and associate the Event Hub Send and Receive role to access the Event Hubs namespace.

We'll then use the Service Principal in Java code to authenticate and send events to the Event Hub.

Create Service Principal

az ad sp create-for-rbac -n eh-service-principal --skip-assignment
{
    "appId": "[AZURE_CLIENT_ID]",
    "displayName": "eh-service-principal",
    "password": "[AZURE_CLIENT_SECRET]",
    "tenant": "[AZURE_TENANT_ID]"
}

Set environment variables for Resource Group and Event Hubs

export RESOURCE_GROUP=anutc-rg
export EVENTHUBS_NAMESPACE=anutc-eh

Obtain ResourceId of Event Hubs namespace, AD Role Id for Event Hubs Send and Receive

EVENTHUBS_ID=$(az eventhubs namespace show --resource-group $RESOURCE_GROUP --name $EVENTHUBS_NAMESPACE -o tsv --query 'id')
echo $EVENTHUBS_ID
EH_SENDER_ROLE_ID=$(az role definition list -n "Azure Event Hubs Data Sender" -o tsv --query '[0].id')
echo $EH_SENDER_ROLE_ID
EH_RCEIVER_ROLE_ID=$(az role definition list -n "Azure Event Hubs Data Receiver" -o tsv --query '[0].id')
echo $EH_RCEIVER_ROLE_ID

Assign the Event Hubs Send and Receive roles to the Service Principal

az role assignment create --assignee $AZURE_CLIENT_ID --role $EH_SENDER_ROLE_ID --scope $EVENTHUBS_ID
az role assignment create --assignee $AZURE_CLIENT_ID --role $EH_RCEIVER_ROLE_ID --scope $EVENTHUBS_ID

Testing role assignment works

We'll test that the Service Principal can obtain the token.

Importing azure identity library

<dependency>
  <groupId>com.azure</groupId>
  <artifactId>azure-identity</artifactId>
  <version>1.7.0-beta.3</version>
</dependency>

Testing token retrieval

import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
ClientSecretCredential credential = new ClientSecretCredentialBuilder()
        .clientId("[AZURE_CLIENT_ID]")
        .clientSecret("[AZURE_CLIENT_SECRET]")
        .tenantId("[AZURE_TENANT_ID]")
        .build();

AccessToken token = credential.getTokenSync(new TokenRequestContext().addScopes("https://eventhubs.azure.net/.default"));

System.out.println("Token:" + token.getToken());

Testing sending events to Event Hubs

Code uses the "CustomEndpointAddress" feature of Event Hubs SDK to communicate to EH behind an Application Gateway with AMQP_WEB_SOCKET transport.

(If no AppGateway is configured, simply remove the "customEndpointAddress(..)" setter in the code below)

(Refer https://gist.github.com/anuchandy/40c67c64f371f38f5ce5de4cd3a52dc5 for details on how to set up the AppGateway to route traffic to Event Hubs)

<dependency>
  <groupId>com.azure</groupId>
  <artifactId>azure-identity</artifactId>
  <version>1.7.0-beta.3</version>
</dependency>
<dependency>
  <groupId>com.azure</groupId>
  <artifactId>azure-messaging-eventhubs-checkpointstore-blob</artifactId>
  <version>1.16.0-beta.1</version>
</dependency>
public static void main(String[] args) throws Exception {
    List<EventData> telemetryEvents = Arrays.asList(
            new EventData("Roast beef".getBytes(UTF_8)),
            new EventData("Cheese".getBytes(UTF_8)),
            new EventData("Tofu".getBytes(UTF_8)),
            new EventData("Turkey".getBytes(UTF_8)));


ClientSecretCredential credential = new ClientSecretCredentialBuilder()
        .clientId("[AZURE_CLIENT_ID]")
        .clientSecret("[AZURE_CLIENT_SECRET]")
        .tenantId("[AZURE_TENANT_ID]")
        .build();


    EventHubProducerClient producer = new EventHubClientBuilder()
            .credential("<eventhubs-namespace>.servicebus.windows.net", "first", credential)
            .transportType(AmqpTransportType.AMQP_WEB_SOCKETS)
            .customEndpointAddress("https://<public-ip-of-app-gateway>") // e.g. "https://20.232.196.115"
            .buildProducerClient();

    EventDataBatch currentBatch = producer.createBatch();

    for (EventData event : telemetryEvents) {
        if (currentBatch.tryAdd(event)) {
            continue;
        }

        producer.send(currentBatch);
        currentBatch = producer.createBatch();

        if (!currentBatch.tryAdd(event)) {
            System.err.printf("Event is too large for an empty batch. Skipping. Max size: %s. Event: %s%n",
                    currentBatch.getMaxSizeInBytes(), event.getBodyAsString());
        }
    }

    producer.send(currentBatch);

    System.out.println("Done Sending!");
}

Note

I've referenced this awesome blog for service principal part https://dzone.com/articles/azure-event-hubs-quotrole-based-access-controlquot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment