· 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.

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.
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
- Download OpenJDK from Adoptium
- Verify installation with
java -version
Install JBang
- Guide: Camel JBang Installation
- JBang allows you to run Camel routes without complex build configurations
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
Select New registration
- Name: CamelDemo
- Account type: Accounts in this organizational directory only
- Click Register
3. Note the IDs
After registration, you’ll find these on the overview page:
- Application (client) ID
- Directory (tenant) ID
4. Create a Client Secret
- Menu: Certificates & secrets → New client secret
- Enter a name and click Add
⚠️ Important: The secret is only displayed once—copy and store it securely immediately
5. Assign Permissions
- Menu: API permissions → Add a permission
- API: Microsoft Graph → Application permissions
- Select
Directory.Read.All
- Click Add permission
- 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
Component | Description | Example |
---|---|---|
Message | Basic unit of data transfer. Contains all information needed for processing | An HTTP request or a file |
Header | Metadata like routing information, auth tokens, or context data | Authorization header for HTTP request authentication, CamelHttpPath as URL path |
Body | The payload (e.g., JSON, XML, CSV) exchanged between systems | {"user": "john.doe", "group": "Developers"} |
How Camel Works
-
Components receive, transform, and route messages (e.g., HTTP, File, Azure)
-
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
-
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:
-
Authentication Part: Handles OAuth login and token management. Secure and efficient authentication is crucial for accessing Microsoft Entra ID data.
-
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.
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?
-
Application Start: The
camel run
command starts the Camel application, loading the application.properties configuration file and the two YAML files -
Authentication: The authentication route executes first to obtain an auth token from Microsoft Entra ID
-
Reading Group Members: Once the auth token is successfully retrieved, the route for reading group members starts, sending requests to the Microsoft Graph API
-
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!