Saturday, September 19, 2026

Building Secure Azure AI Foundry Agents with Managed Identity

 

The credential problem nobody wants to own

Every team that connects an Azure AI Foundry agent to Microsoft Graph, SharePoint, Outlook, or a custom API eventually hits the same question: where do the credentials live?

The usual answers are client secrets, certificates, or a service account password tucked into a config file. All three work. All three also mean somebody now owns a rotation schedule, a secret store, and a risk register entry.

Managed Identity removes the question entirely. Azure gets the token; your agent never sees a secret.

This post walks through configuring an Azure AI Foundry agent to call Microsoft Graph using Managed Identity, including the permission model, the scoping controls, and the three errors you will almost certainly hit on the way.

Why Managed Identity wins

Managed Identity lets an Azure resource request Entra ID tokens on its own behalf. Nothing is stored, nothing expires on your watch.

What you get:

  • No secrets in application code or configuration
  • Token acquisition and renewal handled by the platform
  • A smaller attack surface and a cleaner compliance story
  • Native support for least-privilege access
  • First-class integration across Azure services

What you give up: the illusion that permissions come for free. More on that below.

The request path

Azure AI Foundry Agent
        │
        ▼
  Managed Identity
        │
        ▼
  Microsoft Entra ID
        │
        ▼
   Access Token
        │
        ▼
 Microsoft Graph API
        │
        ▼
Exchange Online / Teams / Users / Files

Step 1: Create a user-assigned managed identity

In the Azure Portal, go to Managed Identities → Create and supply a name, subscription, resource group, and region.

mrclmedpggblweuaip-mi

Once it exists, record three values. You will need all of them later:

  • Client ID
  • Object ID
  • Resource ID

User-assigned identities are the right default for enterprise work. They survive resource deletion, can be shared across services, and give you a stable object to audit.

Step 2: Assign Microsoft Graph permissions

This is the step most people skip, and it is the reason most first attempts fail.

A managed identity is an identity, not an authorization. Creating one grants access to exactly nothing. Microsoft Graph application permissions have to be assigned to it separately, as app role assignments on the Graph service principal.

PermissionPurpose
User.Read.AllSearch and read directory users
Calendars.ReadRead mailbox calendars
Mail.ReadRead mailbox messages
OnlineMeetings.Read.AllRead Teams meeting details
OnlineMeetingTranscript.Read.AllRead meeting transcripts

Then grant admin consent. Without it, every call returns:

Authorization_RequestDenied

Assign only what the agent actually needs. An agent that reads calendars has no business holding Mail.Read.

Step 3: Scope mailbox access (optional but recommended)

Application permissions on Graph are tenant-wide by default. Mail.Read means every mailbox in the organization.

For most enterprises that is far too broad, particularly during a pilot. Application Access Policies in Exchange Online restrict a given application to a specific mail-enabled security group.

A typical pilot limits Calendars.Read and Mail.Read to a small group of approved test users. Any mailbox outside that group returns:

json
{
  "error": {
    "code": "ErrorAccessDenied"
  }
}

Worth noting for troubleshooting: this is a different failure from a missing permission, and it looks different in the response. More on that in the error section.

Step 4: Create the Foundry agent

In Azure AI Foundry, open your project and go to Agents → Create Agent.

Configure the model (GPT-5.6 or whatever your organization has approved), the instructions, the tools, and any knowledge sources.

The instructions matter more than they look. Tell the agent to call tools rather than answer from its own knowledge:

You are an enterprise assistant.

Use the available tools to retrieve user, calendar, email and meeting
information whenever it is required. Always call the appropriate tool
rather than generating the information yourself.

Step 5: Add an OpenAPI tool

Go to Agent → Tools → Add Tool → OpenAPI and set the authentication:

Authentication Method : Managed Identity
Audience              : https://graph.microsoft.com

The audience is what tells Foundry which resource to request a token for. Get it wrong and you will get a 401 that looks like a permissions problem but is not.

Step 6: Start with a diagnostic schema

Resist the urge to paste in your full business schema on the first pass. Register one endpoint, confirm the pipe works end to end, then build on it.

A good first probe:

http
GET /users/{userEmail}/calendar

A successful call confirms three things at once: the managed identity acquired a token, Graph accepted it, and Calendars.Read is present and consented.

Two more worth running:

http
GET /users?$top=1

Validates User.Read.All.

http
GET /users/{userEmail}/messages?$top=1

Validates Mail.Read.

Run each from the Foundry playground and inspect Traces → Tool Execution afterwards. The trace shows the resolved URL, the audience, and the status code, which is where the actual diagnosis happens.

Three errors and what they actually mean

Authorization_RequestDenied (403)

The Graph application permission is missing, or admin consent was never granted.

Check the app role assignment on the managed identity, then check consent. These are two separate things and the second is easy to forget.

ErrorAccessDenied (403)

The permission exists, but the mailbox is outside the Application Access Policy scope.

This is the friendlier failure. Authentication worked and the permission is in place; the policy simply blocked this particular mailbox. Verify the mailbox is in the allowed group.

401 Unauthorized

Authentication itself failed. Usually one of:

  • The managed identity is not assigned to the Foundry resource
  • The audience URL is wrong
  • Token acquisition failed

Nothing about Graph permissions will fix a 401. Work backwards from the identity assignment.

The distinction between the two 403s is the single most useful thing to internalize here. One means "you were never allowed to ask." The other means "you may ask, but not about this mailbox."

Practices worth adopting

Prefer user-assigned identities for anything that will outlive a single resource or be shared across services.

Grant the minimum. Every Graph application permission is tenant-wide until you scope it.

Scope mailboxes with Application Access Policies, especially during pilots. Tenant-wide Mail.Read in a test environment is a finding waiting to happen.

Validate with diagnostic tools before writing business logic. A one-endpoint probe tells you whether the authentication chain works. A full schema tells you nothing until it does.

Document the permission-to-API mapping. When an auditor asks why this identity holds Mail.Read, you want a table, not a recollection.

Avoid client secrets wherever managed identity is supported.

Closing

Managed Identity is the right default for Foundry agents that talk to Microsoft Graph and other enterprise services. It removes secrets from the equation while leaving authentication and authorization where they belong, in Entra ID.

Pairing Foundry agents, managed identity, scoped Graph permissions, and Application Access Policies gives you agents that are useful without being over-privileged, and a governance story you can actually defend.

The setup takes an afternoon. The alternative takes a rotation schedule.

No comments:

Post a Comment

Building Secure Azure AI Foundry Agents with Managed Identity

  The credential problem nobody wants to own Every team that connects an Azure AI Foundry agent to Microsoft Graph, SharePoint, Outlook, or...