Back to Blog

What Is Azure Entra ID? — Identities, Roles, and the Mental Model You Actually Need

Published on 29 April 2026

What Is Azure Entra ID? — Identities, Roles, and the Mental Model You Actually Need
Why identity is more than a login

Imagine you have fifty Azure Functions in production. All of them connect to the same storage account using a connection string that someone pasted into app settings two years ago. Now the security team rotates the storage keys. What happens?

All fifty Functions fail. At the same time. In production.

This is not a contrived example. It happens. And it happens because many developers still treat identity in Azure the way they did a decade ago: username, password, done. In the cloud, that mindset is not merely wrong — it is risky.

Azure Entra ID is the foundation that keeps these failure modes from materializing in the first place — but only if you understand how it actually works, not just which buttons to click in the portal.

This article is not a click-by-click walkthrough, and there are no screenshots. What you get is the mental model you need to design secure architectures, troubleshoot issues, and deliver tangible value in your organization without delay.

What is Azure Entra ID? — A concise overview

Since 2023, Azure Active Directory has officially been named Microsoft Entra ID. The change is more than a rebrand: it signals that Microsoft treats identity as a first-class, cloud-native discipline — distinct from the traditional on-premises Active Directory ecosystem.

Entra ID is the identity backbone for the Microsoft cloud: Microsoft 365, Azure resources, external SaaS applications, and — increasingly — non-human identities such as agents, pipelines, and services.

The critical distinction up front: Entra ID is not a drop-in replacement for on-premises Active Directory. It is a different technology stack. On-premises AD relies on Kerberos, LDAP, and Group Policy. Entra ID relies on OAuth 2.0, OpenID Connect, and REST APIs. The protocols are fundamentally different. Conflate the two, and hybrid designs will not behave the way you expect.

The Entra product family includes several components:

Entra ID is the core: authentication, authorization, single sign-on, and multifactor authentication for your organization. Everything else builds on it.

Entra Workload ID extends the platform to non-human identities — applications, services, and agents. That is the focus of the third post in this series.

Entra ID Governance automates access lifecycle management: who receives which rights, when, and who reviews those grants on a recurring basis.

Entra External ID addresses external scenarios: customers, partners, and business-to-business collaboration.

This post focuses on Entra ID as the core platform and the concepts you will use day to day.

The tenant concept — the foundation for everything else

Before identities, roles, and tokens fall into place, one idea must be solid: the tenant.

A tenant is a dedicated, fully isolated instance of Entra ID for your organization. When you onboard to Azure, Microsoft typically creates a tenant for you automatically. Everything you do in Azure — every resource, every identity, every access path — lives inside that tenant boundary.

The defining property of a tenant is isolation. Identities in Tenant A cannot reach resources in Tenant B without explicit, deliberate configuration. That is not a defect; it is the security model.

The relationship between tenants and subscriptions trips people up early on, so let's be direct: one tenant can own multiple Azure subscriptions. Each subscription belongs to exactly one tenant. A subscription and a tenant are not interchangeable terms.

A useful analogy: the tenant is the office building. Subscriptions are the floors. Resource groups are the departments on each floor. Individual resources are the rooms. Entra ID is the security operation at the entrance.

When do you need more than one tenant? Less often than many teams assume. Typical cases include legally separate group companies, strict regulatory mandates that require data separation, or partner models built on B2B collaboration. For separating production and development workloads, subscriptions usually suffice — you do not need a second tenant.

Identity types: Who — or what — has access?

Entra ID is not limited to user accounts. There is a taxonomy of identity types, and fluency in that taxonomy separates sound architectures from fragile ones.

Human Identities

Users are people with Entra ID accounts: they sign in, complete MFA, and are subject to Conditional Access policies. This is the familiar case.

Groups aggregate users or other security principals. Rather than assigning roles user by user, you assign them to a group — that pattern scales. In enterprise settings, grant roles to groups almost exclusively, not to individual users.

Non-Human / Workload Identities

This is where the interesting — and expensive — mistakes appear.

A service principal is the identity of an application or service registered in Entra ID. When you create an app registration, Entra ID creates a matching service principal. That service principal is what you assign roles to. It carries credentials — either a client secret (effectively a password you must rotate) or a certificate (stronger from a security standpoint, but more demanding to manage).

Managed identities are what you should default to in the vast majority of cases. Under the hood they are still service principals, but Microsoft provisions and rotates credentials for you. You never handle a secret. You never rotate one manually. Nothing expires silently and takes your application offline. It is both the safest and the simplest option in most Azure scenarios.

There are two flavors:

A system-assigned managed identity is bound to a single Azure resource. Delete the Function App, and the identity is removed with it. The relationship is one-to-one: one identity, one resource.

A user-assigned managed identity is its own Azure resource. You create it once and attach it to multiple services. If ten Function Apps must read the same storage account, create one user-assigned identity, grant the role once, and assign that identity to all ten applications. That pattern scales cleanly.

Workload identity federation is the modern path for platforms outside Azure: GitHub Actions, Kubernetes workloads, third-party CI/CD. Instead of storing a long-lived client secret, you establish trust: the external platform presents a short-lived token, and Entra ID exchanges it for an Azure access token. Nothing long-lived needs to sit in a repository or secret store.

System-assigned MI User-assigned MI Service principal
Credentials managed by Microsoft Microsoft You
Lifecycle Tied to resource Independent Independent
Multiple resources No Yes Yes
Typical use Single resource Shared identity Outside Azure / legacy
RBAC — Azure's authorization model

Azure role-based access control (RBAC) is how authorization works for Azure resources. Every call that touches a resource is evaluated through RBAC. The model breaks down into three elements — no more, no less.

Security principal — Who is requesting access? That can be a user, group, service principal, or managed identity.

Role definition — What may that principal do? A role definition bundles permissions expressed as Actions (control-plane operations) and DataActions (data-plane operations). The split matters in practice:

Microsoft.Storage/storageAccounts/write — management action (create or configure the account)

Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read — data action (read blob contents)

Scope — Where does the assignment apply? Azure defines four scopes, from broad to narrow: management group → subscription → resource group → individual resource. Effective permissions inherit downward along that hierarchy. A role assigned at subscription scope automatically covers every resource group and resource beneath it.

Combining those three pieces yields a role assignment. In plain terms: this principal (for example, the Function App's managed identity) may perform these operations (for example, Storage Blob Data Reader) on this scope (for example, storage account rg-prod/sa-data).

As a rule of thumb: choose the narrowest scope that remains operable. If a managed identity only needs read access to one container, assign the role at container scope — not at subscription scope. The extra precision is what separates a defensible design from one that is easy to abuse.

RBAC is additive. Multiple role assignments combine; effective permissions are the union. There is no implicit deny except through explicit deny assignments, which are uncommon.

Keep this boundary clear: Azure RBAC is not the same as Entra ID directory roles. Azure RBAC governs access to Azure resources — storage, virtual machines, Key Vault, and the rest. Directory roles govern administration of Entra ID itself: user lifecycle, role assignment, application registration. The two systems coexist. A Global Administrator in Entra ID does not automatically gain data-plane access to a storage account; that requires an Azure RBAC assignment.

Custom role definition — copy and customize

Instead of granting the broad Contributor role, define a custom role that includes exactly the permissions your workload requires — nothing beyond that.

Azure RBAC custom roles versus Entra ID custom roles
Microsoft offers two different kinds of "custom roles," and they are easy to confuse:
Type Controls access to Licensing
Azure RBAC custom roles Azure resources (storage, Key Vault, virtual machines, and so on) None — included with every subscription
Custom directory roles Entra ID itself (users, applications, groups) Microsoft Entra ID P1 required
The JSON and commands below use Azure RBAC — no P1 license is needed.
json
{
  "Name": "Storage Blob Reader (Custom)",
  "Description": "Reads blobs from defined containers. No writes, no control-plane changes.",
  "Actions": [],
  "NotActions": [],
  "DataActions": [
    "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"
  ],
  "NotDataActions": [],
  "AssignableScopes": [
    "/subscriptions/{your-subscription-id}"
  ]
}
bash
# Create the custom role definition
az role definition create --role-definition role-storage-reader.json

# Assign the role to a managed identity
az role assignment create \
  --assignee <managed-identity-object-id> \
  --role "Storage Blob Reader (Custom)" \
  --scope /subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{sa}
How authentication actually works — tokens and flows

Entra ID does not hand out opaque server sessions in the classical sense — it issues tokens, specifically JSON Web Tokens (JWTs). Modern Azure integrations are token-driven end to end. Internalize that fact, and authentication failures become far easier to triage because you know where to look.

What lives inside a JWT?

A JWT has three Base64-encoded segments separated by dots: header.payload.signature. The payload carries claims: statements about the principal the token represents.

Claims worth knowing cold:

oid — Object ID of the principal in Entra ID. Stable and unique: the identifier you rely on for RBAC bindings and audit trails.

aud — Audience: which resource was this token minted for? A token minted for https://storage.azure.com will be rejected by Microsoft Graph, and vice versa. Each API accepts only tokens targeting itself.

roles — Application roles granted to the principal. For Graph API calls, an empty roles claim often means admin consent for the required permissions is missing.

exp — Token expiration time (Unix epoch seconds). After it passes, callers receive HTTP 401. SDKs normally refresh silently; handcrafted token plumbing often breaks here.

tid — Tenant ID. Essential in multitenant setups to prove the token was issued by the tenant you trust.

HTTP 403 Forbidden usually traces to one of three causes: an incorrect aud claim, missing RBAC for the principal identified by oid, or an empty roles claim when admin consent never ran. Part three of this series walks through diagnosing tokens for service principals and app registrations in detail.
OAuth 2.0 flows — when to use which

OAuth 2.0 is not a single protocol; it is a family of authorization flows. The three you will encounter most often:

Authorization code flow — Human users. The browser redirects to Entra ID, the user signs in, your application exchanges an authorization code for tokens. Conditional Access policies and MFA apply naturally.

Client credentials flow — No signed-in user. The application authenticates as itself — client identifier plus secret or certificate, or a managed identity. Ideal for automation, background tasks, agents, and services.

On-behalf-of flow — Delegation patterns. Service A holds a user token and must call downstream service B without losing user context; Entra issues a new token scoped to service B based on that chain.

For unsupervised workloads — which dominate modern Azure architectures — client credentials (often backed by managed identity) is the appropriate flow.

Sequence diagram: authorization code flow with MFA, token refresh, and API access
Token refresh — proactive versus reactive

Proactive refresh — Before each outbound call, the client checks whether the access token will expire soon (for example, within five minutes) and acquires a replacement quietly. Users never see HTTP 401, and no request fails on expiration. The Microsoft Authentication Library (MSAL), which most Azure-aligned applications rely on, implements this behavior by default where applicable.

Reactive refresh — The client sends whatever token it has until the API returns HTTP 401, then refreshes (or prompts reauthentication) and retries. Simpler to write by hand, but the first request fails unnecessarily.
* The illustration above deliberately follows the reactive path because it exposes each step sequentially.

MSAL blends both strategies in practice: it caches responses, evaluates the exp claim, prefers proactive refreshes when possible, and falls back to reactive recovery when refresh fails. Calling AcquireTokenSilent (or equivalent) lets the library decide whether the cache is still valid.

Remember that refresh tokens also expire. In Entra ID the default sliding lifetime is ninety days while the account remains active. If the refresh token itself lapses without renewal, interactive sign-in is required again.

Managed identity: real credentials without secrets in config

Managed identities answer a question every engineering team should ask: Why are we still shipping secrets alongside our applications?

The answer is: you don't have to — provided the workload runs on an Azure-hosted resource that supports managed identity.

Managed identities integrate with the Azure Instance Metadata Service (IMDS): an HTTP endpoint at http://169.254.169.254/metadata/identity/oauth2/token reachable only from inside the compute resource. The runtime requests tokens there; IMDS forwards the request to Entra ID; no plaintext secret ever crosses your CI pipeline or configuration store.

Client libraries shield you from the raw protocol. The DefaultAzureCredential chain in Azure Identity probes environment variables first (ideal for laptops), managed identity second (ideal for deployments), Azure CLI credentials last as a fallback. One codebase spans local debugging and cloud production environments without divergent branching logic.

Insecure versus secure — the difference in code

Insecure — still common in the wild:

python
# Connection string from app settings — key rotation takes every consumer offline
import os
from azure.storage.blob import BlobServiceClient

connection_string = os.environ["STORAGE_CONNECTION_STRING"]
client = BlobServiceClient.from_connection_string(connection_string)

Secure — the pattern you should standardize on:

python
# Managed identity — no shared secret, no rotation fire drill
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

credential = DefaultAzureCredential()
client = BlobServiceClient(
    account_url="https://{storageaccount}.blob.core.windows.net",
    credential=credential
)

Two lines separate the samples. The security posture is worlds apart.

Enable system-assigned managed identity for a Function App and grant Storage Blob Data Reader without opening the portal:

bash
# Enable system-assigned managed identity
az webapp identity assign \
  --name my-function-app \
  --resource-group rg-prod

# Retrieve the managed identity object ID
PRINCIPAL_ID=$(az webapp identity show \
  --name my-function-app \
  --resource-group rg-prod \
  --query principalId -o tsv)

# Grant Storage Blob Data Reader on the storage account
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Storage Blob Data Reader" \
  --scope /subscriptions/{sub-id}/resourceGroups/rg-prod/providers/Microsoft.Storage/storageAccounts/mystorage
Hands-on: three scripts that surface the truth quickly

No portal clicking. No annotated screenshots. Three commands you can run tomorrow morning to map reality in your tenant and subscriptions.

Important: These scripts perform read-only inspection — they change nothing in Azure. The remediation steps you take afterward can still affect production systems. Before revoking roles, switching identities, or tightening scopes, plan the change, validate in a non-production environment, and align with your operations team.
Script 1: Service principals with excessive subscription-level roles
bash
# Service principals granted Owner or Contributor at subscription scope
az role assignment list \
  --scope /subscriptions/{subscription-id} \
  --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor'] | [?principalType=='ServicePrincipal']" \
  --output table

Follow up by reviewing each hit. Does that automation account truly require Contributor or Owner? In most scenarios a narrower built-in role or a custom definition with two or three granular permissions suffices.

Script 2: Function Apps without managed identity enabled
bash
# List Function Apps inside a subscription
az functionapp list --query "[].{Name:name, RG:resourceGroup, Identity:identity.type}" --output table

# Rows with no assigned identity
az functionapp list \
  --query "[?identity==null].{Name:name, ResourceGroup:resourceGroup}" \
  --output table

Treat the results as your backlog. Every Function App lacking identity is a candidate for managed identity migration — you already know exactly where remediation starts.

Script 3: Export RBAC assignments to CSV for compliance evidence
powershell
# Export every role assignment in the current subscription to CSV
Get-AzRoleAssignment | Select-Object `
  DisplayName, `
  ObjectType, `
  RoleDefinitionName, `
  Scope | Export-Csv -Path "rbac-audit-$(Get-Date -Format 'yyyy-MM-dd').csv" -NoTypeInformation

Write-Host "Export complete."

Attach the spreadsheet to security reviews or audits. Scheduling a quarterly export and archiving it alongside infrastructure-as-code snapshots gives you a defensible RBAC history.

Five recurring mistakes — and how to dodge them
Mistake 1: Granting Contributor because the deadline is tight

What breaks: The workload inherits permission to modify, delete, or create anything in the resource group. One compromised component endangers the entire footprint.

Better path: Choose a purpose-built built-in role or craft a custom role that lists only the Actions and DataActions you need.

Mistake 2: Storage connection strings in application settings

What breaks: Key rotation becomes a coordinated outage. Secrets leak into logs. Anyone with configuration access holds production credentials.

Better path: Pair managed identity with Azure Key Vault references for remaining secrets. Remove static connection strings from app settings entirely.

Mistake 3: One service principal shared across unrelated workloads

What breaks: A breach in one system yields keys to every resource that principal can touch, and audit logs rarely attribute activity to a specific workload.

Better path: Scope identities per workload or logical service. Share a user-assigned managed identity across cooperating resources only when they truly represent one trust boundary.

Mistake 4: Client secrets without monitoring expiration

What breaks: A credential silently expires Friday evening; production stalls; triage wastes hours tracing HTTP 401 responses.

Better path: Raise Azure Monitor alerts for upcoming secret expiration — or migrate to managed identity so expiry is no longer your problem.

Mistake 5: Subscription-wide scopes when resource-level suffices

What breaks: Readers can enumerate every storage account in the subscription — including workloads that should remain isolated from one another.

Better path: Start at the resource or container boundary whenever operationally feasible. When uncertain, narrower scope is almost always the safer choice.

Closing thoughts

Three principles worth memorizing:

Managed identity beats service principal beats connection string. That ordering holds without exception for Azure-hosted code paths.

RBAC is a conceptual model, not a portal wizard. Master principal, role, and scope, and you will make sound authorization calls in minutes instead of hunting through blade after blade.

Tokens are the contract. Fluency in aud, oid, and roles tells you exactly where to start when authentication misbehaves.