· Simon Hartl · System Integration  · 14 min read

Apache Camel & Microsoft Entra ID: A Professional Guide to Reading Group Members

Learn how to efficiently read Microsoft Entra ID group members (including nested memberships) using Apache Camel and Microsoft Graph API. Our battle-tested solution combines OAuth2 authentication, YAML routes, and CSV export for professional system integration.

Learn how to efficiently read Microsoft Entra ID group members (including nested memberships) using Apache Camel and Microsoft Graph API. Our battle-tested solution combines OAuth2 authentication, YAML routes, and CSV export for professional system integration.

Practical System Integration: Apache Camel and Microsoft Entra ID

German version is available

Introduction: Mastering Complex Scenarios with Apache Camel

Apache Camel is a flexible open-source integration framework for the Java platform that simplifies data exchange between systems—from simple file operations to complex API integrations. While many tutorials only cover basic examples like reading files or simple data processing, this article takes it further: We’ll build a production-ready Camel integration that reads members of a Microsoft Entra ID group (formerly Azure Active Directory)—including nested memberships—and stores them in a CSV file. By combining Camel routes with custom code, we’ll implement API authentication, data filtering, and file output in an elegant way.

Integration of Microsoft Entra ID via Graph API with Apache Camel for CSV output

By the end of this article, you’ll not only have a better understanding of Apache Camel but also know how to implement sophisticated integrations with Microsoft Entra ID in your own projects.

Who Is This Article For?

This guide is designed for developers and IT professionals who have basic knowledge of Java or similar programming languages and want to expand their system integration skills.

We’ll cover not just Camel basics but also advanced techniques like OAuth authentication, API handling, and custom code integration. You can follow each step using the provided code examples and configuration files.

Preparation

Installing Apache Camel

Apache Camel is a powerful open-source integration framework for the Java platform. For this example, we’re using Camel 4.10 (LTS) with Java 21+.

Install Java

Install JBang

Set Up Camel CLI

  • Install the Camel command using JBang:
jbang app install camel@apache/camel

This command downloads the required components and makes the camel command available.

Result

You can now execute Camel routes and Java code directly without needing Maven or Gradle project structures.

Preparing Microsoft Entra ID

To allow Camel to access Microsoft Entra ID (formerly Azure Active Directory), you need to register an application and assign permissions.

1. Sign In

Log in to the Microsoft Entra Admin Center

2. Register an Application

Navigate to Microsoft Entra ID → App registrations

Entra Side Menu

Select New registration

Entra App Registration

  • Name: CamelDemo
  • Account type: Accounts in this organizational directory only
  • Click Register

Register Entra App

3. Note the IDs

After registration, you’ll find these on the overview page:

  • Application (client) ID
  • Directory (tenant) ID

Application information including Client ID

4. Create a Client Secret

  • Menu: Certificates & secretsNew client secret

Create client secret

  • Enter a name and click Add

Client secret details

⚠️ Important: The secret is only displayed once—copy and store it securely immediately

Client secret value

5. Assign Permissions

  • Menu: API permissionsAdd a permission

API permission

  • API: Microsoft GraphApplication permissions
  • Select Directory.Read.All
  • Click Add permission

Add permission

  • Grant admin consent Grant admin consent

💡 Security Tip: Never store client_secret directly in a public repository. Use environment variables or a secrets manager.

With this preparation complete, Camel and Microsoft Entra ID are ready for route development.

Developing the Route

Now that our environment is set up, we can focus on developing the Camel route. Our goal is to create a production-ready solution that considers logging, efficiency, and reusability. We’ll build the route in modular parts:

Apache Camel Crash Course - Message-Based Integration Explained

Apache Camel is a flexible open-source integration framework for Java that simplifies data exchange between systems—from simple file operations to complex API integrations (e.g., with Microsoft Entra ID or REST services).

Core Camel Concepts

ComponentDescriptionExample
MessageBasic unit of data transfer. Contains all information needed for processingAn HTTP request or a file
HeaderMetadata like routing information, auth tokens, or context dataAuthorization header for HTTP request authentication, CamelHttpPath as URL path
BodyThe payload (e.g., JSON, XML, CSV) exchanged between systems{"user": "john.doe", "group": "Developers"}

How Camel Works

  1. Components receive, transform, and route messages (e.g., HTTP, File, Azure)

  2. Routes define the data flow—for example:

    • Read a file → Transform data → Send to an API
    • Query an API (e.g., Microsoft Graph) → Filter response → Save as CSV
  3. Advantages:

    • Flexible: Over 300 pre-built components (e.g., for OAuth2, JMS, Kafka)
    • Extensible: Custom logic can be added using Java, Groovy, or scripting languages
    • Simple: Routes can be defined in Java, XML, or YAML (as in this tutorial)

Why Camel?

Camel’s message-based design makes it easily adaptable to different requirements—ideal for system integration, ETL processes, or microservices.

Route Concept

Our solution consists of multiple routes:

  1. Authentication Part: Handles OAuth login and token management. Secure and efficient authentication is crucial for accessing Microsoft Entra ID data.

  2. Worker Part: Responsible for reading groups and querying group members. This is where the actual work happens after successful authentication.

This modular approach ensures each route part is clearly defined and reusable, simplifying maintenance, testing, and debugging.

Configuration Files

To make our solution configurable and flexible, we store parameters in a configuration file called application.properties:

# Authentication URI
ms.login.uri = https://login.microsoftonline.com/{{ms.graph.tenant_id}}/oauth2/v2.0/token
# Number of seconds before token expiration to request a new token
token_grace_period = 60

# Name of the group to fetch
ad_group.display_name = TestGroup

# Application configuration
ms.graph.tenant_id = REPLACE_WITH_YOUR_TENANT_ID
ms.graph.client_id = REPLACE_WITH_YOUR_CLIENT_ID
ms.graph.client_secret = REPLACE_WITH_YOUR_CLIENT_SECRET

In this file, make the following changes:

  • ms.graph.tenant_id: Replace with your tenant ID (found in the “Directory (tenant) ID” field)

  • ms.graph.client_id: Replace with your application ID (found in the “Application (client) ID” field)

  • ms.graph.client_secret: Replace with the client secret you created

  • ad_group.display_name: Enter the name of the group whose members you want to read

⚠️ WARNING: When using a source code repository like Git, ensure secrets are never committed. Use environment variables or secure secret management systems to store sensitive information.

Developing the Authentication Route

Authentication is a core component of our application. We manage the necessary parameters through application.properties. In this section, we’ll create a route that handles login and token retrieval, defined in auth-handler.camel.yaml.

Route Concept

The auth handler is responsible for providing an auth token. To improve efficiency, tokens are cached so a new login isn’t required for every call. If a token exists, we check its validity. A new login is automatically performed a configurable number of seconds before expiration (controlled by token_grace_period).

A key feature of this route is the simple integration of Groovy code, which keeps the logic clear and maintainable. This demonstrates one of Camel’s strengths: easily embedding custom code to meet specific requirements that go beyond standard functions.

Route Implementation

The route code can be found at auth-handler.camel.yaml.

What this section does: The route-get-token route checks if a valid OAuth token exists in cache. If not, it requests a new token from Microsoft Entra ID and stores it.

- route:
    id: route-get-token
    from:
      uri: direct
      parameters:
        name: getToken
      steps:
        - script:
            expression:
              groovy:
                expression: |
                  long now = (long)System.currentTimeMillis()/1000
                  long expiresAt = exchange.getVariable("route:expiresAt") ?: 0
                  long gracePeriod = {{token_grace_period}}
                  boolean expired = (now + gracePeriod) >= expiresAt
                  exchange.setProperty('now', now)
                  exchange.setProperty('expiresAt', expiresAt)
                  exchange.setProperty('expiresIn', expiresAt-now)
                  exchange.setProperty('gracePeriod', gracePeriod)
                  exchange.setProperty('expired', expired)
        - log:
            loggingLevel: INFO
            message: "Checking token expiration: now: ${exchangeProperty.now}, expiresAt: ${exchangeProperty.expiresAt}, gracePeriod: ${exchangeProperty.gracePeriod}, expired: ${exchangeProperty.expired}"
        - choice:
            otherwise:
              description: use existing token
              steps:
                - log:
                    message: Using cached token (expires in ${exchangeProperty.expiresIn} seconds).
            when:
              - description: retrieve a new token
                steps:
                  - log:
                      loggingLevel: INFO
                      message: Token is expired or about to expire. Fetching a new one.
                  - to:
                      uri: direct
                      parameters:
                        name: fetchToken
                  - script:
                      expression:
                        groovy:
                          expression: |
                            long expiresAt = (System.currentTimeMillis()/1000) + body.expires_in

                            exchange.setProperty('expiresAt', expiresAt)
                            exchange.setProperty('expiresIn', body.expires_in)
                            exchange.setVariable('route:expiresAt', expiresAt)
                            exchange.setVariable('route:token', body.access_token)

                            exchange.in.body = null
                  - log:
                      message: New token obtained successfully (expires in ${exchangeProperty.expiresIn} seconds).
                expression:
                  groovy:
                    expression: exchangeProperty.expired
        - removeHeaders:
            pattern: "*"
        - setHeader:
            expression:
              groovy:
                expression: "\"Bearer ${exchange.getVariable('route:token')}\""
            name: Authorization

Summary:

  • Checks expiration time (expiresAt) against current time
  • Calls fetchToken if needed
  • Stores new token in variables (route:token) for future API calls

The second route in the same file handles actual token retrieval:

- route:
    id: route-fetch-token
    from:
      uri: direct
      parameters:
        name: fetchToken
      steps:
        - setBody:
            expression:
              simple:
                expression: client_id={{ms.graph.client_id}}&scope=https://graph.microsoft.com/.default&client_secret={{ms.graph.client_secret}}&grant_type=client_credentials
        - to:
            uri: https
            parameters:
              httpMethod: POST
              httpUri: "{{ms.login.uri}}"
        - unmarshal:
            json: {}

Summary:

  • Builds HTTP POST request with client_id, client_secret, and scope
  • Sends request to ms.login.uri
  • Parses JSON response

Route for Reading Group Members

With a valid auth token, we can now tackle the main task: reading members of a specific Microsoft Entra ID group. This route is defined in a YAML file and uses the previously obtained token to access the Microsoft Graph API and extract group members.

Route Concept

This demo uses a timer with a single execution (repeatCount: 1) to start the process. In production, you could configure this timer to run daily for regular data updates.

First, we retrieve the auth token as described in the authentication route. This token is needed to authenticate with the Microsoft Graph API and access group data.

We then send an HTTP request to the Microsoft Graph API to find the group by its display name. Once we have the group ID, we send another request to retrieve all members, including nested members.

A special feature of this route is result filtering to only include users. This is done via a Groovy script block that filters the API response to return only entries of type “User”. For group members, we write the id, displayName, and userPrincipalName to a CSV file named group-members.csv.

Route Implementation

The YAML file for this route can be found here: list-group-members.camel.yaml.

What this section does: This route starts once via timer, gets the auth token, determines the group ID from the name, and retrieves all members. Only users are filtered and saved to group-members.csv.

- route:
    id: route-4231
    from:
      uri: timer
      parameters:
        period: "1000"
        repeatCount: "1"
        timerName: timer
      steps:
        - to:
            uri: direct
            parameters:
              name: getToken
              timeout: "5000"
        - to:
            uri: https
            parameters:
              httpMethod: GET
              httpUri: https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '{{ad_group.display_name}}'&$select=id,displayName
        - unmarshal:
            json:
              prettyPrint: false
        - setProperty:
            expression:
              simple:
                expression: ${body[value][0][id]}
            name: group-id
        - removeHeaders:
            excludePattern: Authorization
            pattern: "*"
        - setHeaders:
            headers:
              - expression:
                  simple:
                    expression: /v1.0/groups/${exchangeProperty.group-id}/transitiveMembers
                name: CamelHttpPath
              - expression:
                  constant:
                    expression: $select=id,userPrincipalName,displayName
                name: CamelHttpQuery
        - to:
            uri: https
            parameters:
              httpMethod: GET
              httpUri: https://graph.microsoft.com
        - unmarshal:
            json: {}
        - setBody:
            expression:
              simple:
                expression: ${body[value]}
        - setBody:
            description: Filter for users only
            expression:
              groovy:
                expression: |-
                  body.findAll { entry ->
                    entry.'@odata.type' == '#microsoft.graph.user'
                  }
        - marshal:
            csv:
              header:
                - id
                - displayName
                - userPrincipalName
              headerDisabled: false
        - to:
            uri: file
            parameters:
              directoryName: "."
              fileName: group-members.csv
        - log:
            message: Group members of {{ad_group.display_name}} written to ${header.CamelFileNameProduced}

Summary:

  • Timer triggers once (repeatCount=1)
  • Retrieves auth token (getToken)
  • Gets group ID from displayName
  • Calls /transitiveMembers to include nested memberships
  • Filters for users only (#microsoft.graph.user)
  • Exports results to group-members.csv

This route demonstrates how easily Apache Camel can solve complex integration tasks and process data from various sources. Using Groovy scripts and flexible route configuration, we can precisely control data processing while maintaining code readability and maintainability.

Graphical Tools for Visualizing and Editing Routes

For both beginners and experienced developers, graphical tools can significantly simplify working with Apache Camel. They provide visual representations of routes and allow intuitive creation, modification, and debugging of integration scenarios.

Particularly useful are plugins for VS Code, such as Kaoto and Camel Karavan. These plugins offer graphical interfaces that enable drag-and-drop creation and editing of Camel routes, making it easier to understand and maintain routes—especially for those new to developing integration solutions with Camel.

Screenshot of Auth Handler Route

Benefits of Graphical Tools

  • Easier Onboarding: Graphical tools provide an intuitive interface that simplifies getting started with Camel route development
  • Visual Representation: Visualizing routes helps better understand and debug complex integration scenarios
  • Efficient Editing: Drag-and-drop and other graphical functions enable faster creation and modification of routes

These tools are particularly valuable for beginners getting familiar with Apache Camel, but experienced developers can also benefit from the visual representations and editing capabilities.

Invoking the Route

Now that we’ve defined the routes for authentication and reading group members, let’s see them in action. Invoke the route from the command line with:

camel run application.properties auth-handler.camel.yaml list-group-members.camel.yaml

When executing the above command, you’ll see output similar to:

Running integration with the following configuration:
    --camel-version=4.10.6
2025-08-07 16:16:23.726 INFO 30040 --- [ main] org.apache.camel.main.MainSupport : Apache Camel (JBang) 4.10.6 is starting
2025-08-07 16:16:23.879 INFO 30040 --- [ main] org.apache.camel.main.MainSupport : Running Mac OS X 15.5 (aarch64)
2025-08-07 16:16:23.879 INFO 30040 --- [ main] org.apache.camel.main.MainSupport : Using Java 21.0.2 (OpenJDK 64-Bit Server VM) with PID 30040
2025-08-07 16:16:23.928 INFO 30040 --- [ main] org.apache.camel.main.BaseMainSupport : Properties location: file:application.properties
2025-08-07 16:16:24.373 INFO 30040 --- [ main] he.camel.cli.connector.LocalCliConnector : Camel JBang CLI enabled
2025-08-07 16:16:24.848 INFO 30040 --- [ main] .main.download.MavenDependencyDownloader : Downloaded: org.apache.camel:camel-csv:4.10.6 (took: 303ms) from: central@https://repo1.maven.org/maven2
2025-08-07 16:16:24.867 INFO 30040 --- [ main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.10.6 (auth-handler) is starting
2025-08-07 16:16:25.055 INFO 30040 --- [ main] e.camel.impl.engine.AbstractCamelContext : Routes startup (total:3)
2025-08-07 16:16:25.055 INFO 30040 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route-get-token (direct://getToken)
2025-08-07 16:16:25.055 INFO 30040 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route-fetch-token (direct://fetchToken)
2025-08-07 16:16:25.055 INFO 30040 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route-4231 (timer://timer)
2025-08-07 16:16:26.447 INFO 30040 --- [-timer://timer] auth-handler.camel.yaml:23 : Checking token expiration: now: 1754576186, expiresAt: 0, gracePeriod: 60, expired: true
2025-08-07 16:16:26.455 INFO 30040 --- [-timer://timer] auth-handler.camel.yaml:38 : Token is expired or about to expire. Fetching a new one.
2025-08-07 16:16:26.790 INFO 30040 --- [-timer://timer] auth-handler.camel.yaml:64 : New token obtained successfully (expires in 3599 seconds).
2025-08-07 16:16:27.270 INFO 30040 --- [-timer://timer] list-group-members.camel.yaml:73 : Group members of TestGroup written to ./group-members.csv

For rapid development and testing, you can enable developer mode by adding the --dev option:

camel run --dev application.properties auth-handler.camel.yaml list-group-members.camel.yaml

In developer mode, Camel monitors the specified files and automatically restarts the integration when you save changes. This eliminates the need to manually stop and restart the application—ideal for quickly testing configuration adjustments, route changes, or new code snippets.

What Happens During Invocation?

  1. Application Start: The camel run command starts the Camel application, loading the application.properties configuration file and the two YAML files

  2. Authentication: The authentication route executes first to obtain an auth token from Microsoft Entra ID

  3. Reading Group Members: Once the auth token is successfully retrieved, the route for reading group members starts, sending requests to the Microsoft Graph API

  4. Saving Results: The filtered data is written to a CSV file. After successful execution, you’ll find the group members in the group-members.csv file in your working directory

The Camel application continues running after processing the route/timer event to handle additional requests or tasks. You can terminate the application at any time by pressing Ctrl-C in the terminal.

Result

After successfully executing the route, you’ll find the group-members.csv file in your working directory. This file contains a list of all group members with the following information:

  • id: The user’s unique ID
  • displayName: The user’s display name
  • userPrincipalName: The user’s username

With these simple commands and defined routes, you can ensure the correct version of Camel is used to read Microsoft Entra ID group members and store them in a CSV file. This demonstrates how easily and efficiently complex integration tasks can be solved with Apache Camel.

Conclusion: Apache Camel for Professional Microsoft Entra ID Integrations

In this article, we’ve shown how to develop a robust and efficient solution for integrating with Microsoft Entra ID (formerly Azure AD) using Apache Camel. By combining Camel routes, custom Groovy code, and the Microsoft Graph API, we created an application that reads group members (including nested memberships) and stores them in a CSV file.

We covered not just Apache Camel basics but also advanced techniques like:

  • OAuth2 authentication with Client Credentials Flow and token caching
  • Modular YAML routes for maintainability and scalability
  • Data filtering and CSV export using Camel’s marshaling components

Apache Camel proves to be a powerful integration framework for enterprise scenarios—whether for system integration, automation, or ETL processes. Its flexibility (with over 300 components) and extensibility (with Groovy or Java) make it the ideal choice for demanding integration tasks with Microsoft Entra ID and other cloud services.

Next Steps: Implement Your Own Integration

The best practices and code examples presented here are available on GitHub: 👉 GitHub Repository: Camel & Microsoft Entra ID Integration

Clone the repository, adjust the application.properties file, and start your route with:

camel run application.properties auth-handler.camel.yaml list-group-members.camel.yaml

Want to Learn More About Apache Camel?

  • Experiment with the examples and adapt them to your Microsoft 365 environment
  • Follow our blog
  • Need support? We’d be happy to help you implement your Camel integration—from concept to operation. Contact us for a free initial consultation.

With the techniques and tools presented here (like Kaoto or Camel Karavan), you’re now well-equipped to develop your own integration solutions—whether for Microsoft Entra ID, Azure AD, or other API-based systems. Good luck with your projects!

Back to Blog

Related Posts

View All Posts »

Systemintegration muss nicht teuer sein

Viele Unternehmen scheuen vor Systemintegration zurück – aus Angst vor hohen Kosten und komplexer Umsetzung. Der Artikel zeigt, wie das Open-Source-Framework Apache Camel eine kostengünstige, flexible und zukunftssichere Lösung bietet. Mit standardisierten Integrationsmustern, hoher Wiederverwendbarkeit und einfacher Wartung ermöglicht Camel auch kleinen und mittleren Unternehmen den Einstieg in eine professionelle Integrationsarchitektur – ohne Vendor-Lock-in oder Lizenzkosten.