Thursday, October 15, 2026

From Azure Functions to APIM and Microsoft Graph: A Hands-On Architecture Journey

October 2026

Today was one of those rewarding deep-dive sessions where theory met practice. The goal was simple: understand how Azure API Management (APIM), Azure Functions, Microsoft Entra ID tokens, and Microsoft Graph can work together in a real-world enterprise authorization scenario.

The Starting Point: Building My First Java Azure Function

I started by creating a Java Azure Function locally, deploying it to Azure, and verifying that it could be invoked through a browser.

@FunctionName("HttpExample")
public HttpResponseMessage run(...)

Successfully deploying and invoking this function validated:

  • Java Azure Functions runtime
  • Azure deployment process
  • HTTP triggers
  • Azure Function hosting
  • Public endpoint accessibility

Moving Back to APIM

The real objective was not the function itself. The objective was to build and understand a scalable enterprise authorization architecture.

Consumer
    ↓
APIM
    ↓
Azure Function
    ↓
Microsoft Graph
    ↓
Authorization Decision
    ↓
Backend API

This is a common enterprise pattern where APIM acts as a gateway and Azure Functions perform custom authorization or enrichment logic.

Calling Azure Function from APIM

I implemented an APIM policy using send-request.

<send-request mode="new"
              response-variable-name="userContext">

    <set-url>
        https://functionapp.azurewebsites.net/api/HttpExample
    </set-url>

    <set-method>POST</set-method>

</send-request>

This was my first successful proof that:

APIM
   ↓
Azure Function
   ↓
Response
   ↓
APIM

works seamlessly.

The Function response was captured inside:

context.Variables["userContext"]

Understanding APIM send-request

One of the key learnings was that APIM can act as an HTTP client itself.

The send-request policy allows APIM to invoke:

  • Azure Functions
  • REST APIs
  • Microsoft Graph
  • Internal Services
  • Validation Services

without forwarding the request to the backend.

Building a Validation Function

Next, I added a second Azure Function:

@FunctionName("ValidateApp")

The goal was:

  1. Receive a Bearer Token
  2. Decode JWT
  3. Extract Application ID
  4. Call Microsoft Graph
  5. Retrieve Application Name

Receiving Authorization Headers

Initially the function returned:

Authorization header missing

Investigation revealed something important.

authorization=Bearer eyJ...

instead of:

Authorization=Bearer eyJ...

This demonstrated a critical concept:

HTTP Header names are case-insensitive.

The safest implementation was:

String authHeader = request.getHeaders()
    .entrySet()
    .stream()
    .filter(h -> h.getKey().equalsIgnoreCase("Authorization"))
    .map(Map.Entry::getValue)
    .findFirst()
    .orElse(null);

Decoding JWT Tokens

I used:

DecodedJWT jwt = JWT.decode(token);

An important realization:

JWT.decode() does not validate the token. It simply decodes the payload and makes claims available.

Useful claims included:

appid
tid
aud
roles

Why APIM Should Validate Tokens

In an enterprise architecture, JWT validation should not happen inside every service. Instead:

Consumer
   ↓
APIM
   ↓
validate-jwt
   ↓
Azure Function

APIM validates:

  • Signature
  • Issuer
  • Audience
  • Expiration

Once APIM validates the token, downstream services can trust it.

Discovering the Calling Application

A client-credentials token contained:

{
  "appid":"4fa96e1a-2762-416b-b81e-bfea8b3e66de"
}

The Azure Function extracted it using:

String appId =
    jwt.getClaim("appid").asString();

with fallback support for:

azp

Microsoft Graph Integration

The next step was querying Microsoft Graph.

appid
   ↓
Microsoft Graph
   ↓
Service Principal
   ↓
Display Name

Implementation:

TokenCredential credential =
    new DefaultAzureCredentialBuilder().build();

GraphServiceClient graphClient =
    new GraphServiceClient(credential);

Managed Identity Learnings

The preferred enterprise approach is:

  • Enable Function App Managed Identity
  • Avoid client secrets
  • Use DefaultAzureCredential
  • Authenticate to Graph securely

Benefits include:

  • No secret management
  • Automatic credential rotation
  • Improved security posture
  • Native Azure integration

Required Graph Permissions

To query Service Principals, the Function Managed Identity requires:

  • Application.Read.All
  • or Directory.Read.All

with administrator consent granted.

APIM Debugging Techniques

One useful debugging technique was returning the Authorization header directly:

<return-response>
    <set-body>
        @(context.Request.Headers.GetValueOrDefault("Authorization"))
    </set-body>
</return-response>

This quickly proves whether APIM is receiving the token.

Liquid Templates vs APIM Expressions

Another interesting topic explored was:

template="liquid"

Liquid templates are useful for:

  • JSON Transformation
  • XML Transformation
  • SOAP to REST conversion
  • Payload reshaping

Whereas APIM expressions:

@(...)

are used for:

  • Variables
  • Headers
  • Request Context
  • Conditional Logic

The Emerging Architecture

Consumer
    ↓
APIM
    ↓
JWT Validation
    ↓
Azure Function
    ↓
Extract appid
    ↓
Microsoft Graph
    ↓
Lookup Service Principal
    ↓
Authorization Decision
    ↓
Java Backend API

This pattern centralizes authorization and keeps backend services simple.

Key Takeaways

Technical Learnings

  • Built and deployed a Java Azure Function
  • Invoked Functions from APIM
  • Forwarded Bearer tokens through APIM
  • Decoded JWT tokens
  • Extracted appid claims
  • Integrated with Microsoft Graph
  • Explored Managed Identity authentication
  • Learned Graph permission requirements
  • Understood APIM policy debugging
  • Learned the difference between Liquid templates and APIM expressions

Architectural Learnings

The biggest realization was:

APIM should own API governance and token validation, while Azure Functions should handle custom authorization and business-specific logic.

This creates a scalable and reusable enterprise architecture.

What's Next?

APIM
    ↓
validate-jwt
    ↓
Azure Function
    ↓
Microsoft Graph
    ↓
Retrieve Entra Groups
    ↓
Inject Headers
       X-App-Name
       X-User-Groups
    ↓
Backend API

At that point, APIM becomes a true enterprise gateway and Azure Functions become a reusable authorization service for the entire platform.

A productive day of learning, troubleshooting, architecture design, Azure integration, and real-world API security patterns.

No comments: