Skip to main content
AzureSecurityintermediate

Microsoft Entra ID (Azure AD)

Guide to Microsoft Entra ID covering app registrations, Conditional Access policies, Privileged Identity Management, B2B/B2C scenarios, and security best practices.

CloudToolStack Team24 min readPublished Mar 14, 2026

Prerequisites

  • Azure subscription with Entra ID tenant
  • Basic understanding of authentication protocols (OAuth 2.0, OIDC)
  • Familiarity with Azure portal navigation

Introduction to Microsoft Entra ID

Microsoft Entra ID (formerly Azure Active Directory or Azure AD) is Microsoft's cloud-based identity and access management service. It is the backbone of authentication and authorization for every Microsoft cloud service, including Azure, Microsoft 365, Dynamics 365, and thousands of third-party SaaS applications. If you use Azure, you are already using Entra ID whether you realize it or not: every Azure subscription is associated with an Entra ID tenant.

Entra ID is fundamentally different from on-premises Active Directory Domain Services (AD DS). While AD DS uses LDAP, Kerberos, and Group Policy, Entra ID uses modern protocols: OAuth 2.0, OpenID Connect (OIDC), and SAML 2.0. There are no domain controllers, no organizational units (in the AD DS sense), and no Group Policy Objects. Instead, Entra ID provides app registrations, Conditional Access policies, Privileged Identity Management (PIM), and identity governance features designed for a cloud-first world.

This guide covers the essential Entra ID concepts and configurations every Azure engineer needs: tenant management, user and group management, app registrations and service principals, Conditional Access policies, Privileged Identity Management, B2B and B2C scenarios, and security best practices. All examples use the Azure CLI and Microsoft Graph API.

Entra ID Licensing

Entra ID comes in four tiers: Free (included with Azure), P1 ($6/user/month), P2 ($9/user/month), and Governance (add-on). Free includes basic user management and SSO. P1 adds Conditional Access and group-based licensing. P2 adds PIM, Identity Protection, and access reviews. Most production environments need at least P1 for Conditional Access. P2 is recommended for environments with privileged roles.

Tenant and Directory Fundamentals

An Entra ID tenant is a dedicated instance of the directory that your organization receives when it signs up for a Microsoft cloud service. Each tenant is a security boundary: users, groups, and applications in one tenant are completely isolated from those in another tenant. A tenant is identified by a unique tenant ID (GUID) and one or more domain names.

Every Azure subscription must be associated with exactly one Entra ID tenant for authentication. Multiple subscriptions can share the same tenant (common in enterprises), and you can transfer subscriptions between tenants if needed. The tenant that owns your subscriptions is your primary identity boundary.

bash
# Get your current tenant information
az account show --query '{TenantId:tenantId, Subscription:name}' -o table

# List all tenants your account has access to
az account tenant list -o table

# Get tenant details using Microsoft Graph
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/organization" \
  --query "value[0].{DisplayName:displayName, TenantId:id, Domains:verifiedDomains[].name}"

# Create a user
az ad user create \
  --display-name "Jane Doe" \
  --user-principal-name "jane.doe@contoso.com" \
  --password "InitialP@ssw0rd!" \
  --force-change-password-next-sign-in true \
  --mail-nickname "jane.doe"

# Create a security group
az ad group create \
  --display-name "Cloud Engineers" \
  --mail-nickname "cloud-engineers" \
  --description "Cloud engineering team members"

# Add user to group
az ad group member add \
  --group "Cloud Engineers" \
  --member-id $(az ad user show --id "jane.doe@contoso.com" --query id -o tsv)

# List all users (with filtering)
az ad user list \
  --filter "department eq 'Engineering'" \
  --query "[].{Name:displayName, UPN:userPrincipalName, Created:createdDateTime}" \
  -o table

App Registrations and Service Principals

App registrations are the most important concept to understand when integrating applications with Entra ID. An app registration defines your application's identity configuration: what permissions it needs, how users authenticate with it, and what tokens it receives. When you create an app registration, Entra ID automatically creates a corresponding service principal in your tenant.

The relationship between app registrations and service principals confuses many people. Think of the app registration as a global template (it can exist in one tenant but be used by many), and the service principal as the local instance of that application in a specific tenant. When you consent to a third-party app, a service principal is created in your tenant to represent that app.

bash
# Create an app registration for a web application
APP_ID=$(az ad app create \
  --display-name "My Cloud Dashboard" \
  --sign-in-audience "AzureADMyOrg" \
  --web-redirect-uris "https://dashboard.contoso.com/auth/callback" \
  --enable-id-token-issuance true \
  --query appId -o tsv)

echo "Application (client) ID: $APP_ID"

# Create a client secret (for confidential clients)
az ad app credential reset \
  --id $APP_ID \
  --display-name "production-secret" \
  --end-date "2027-03-14" \
  --query '{ClientId:appId, SecretValue:password, Expiry:endDate}'

# Create a service principal for the app
SP_ID=$(az ad sp create --id $APP_ID --query id -o tsv)

# Assign API permissions (Microsoft Graph: User.Read)
az ad app permission add \
  --id $APP_ID \
  --api 00000003-0000-0000-c000-000000000000 \
  --api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope

# Grant admin consent for the permissions
az ad app permission admin-consent --id $APP_ID

# Create a managed identity (recommended over app registrations for Azure resources)
az identity create \
  --name "dashboard-identity" \
  --resource-group "rg-dashboard" \
  --query '{ClientId:clientId, PrincipalId:principalId}' -o table

Prefer Managed Identities Over Client Secrets

For applications running on Azure (VMs, App Service, Functions, AKS), always use Managed Identities instead of app registrations with client secrets. Managed Identities provide automatic credential rotation, no secrets to manage or leak, and seamless integration with Azure RBAC. There are two types: system-assigned (tied to a single resource) and user-assigned (can be shared across resources).

Authentication Flows

FlowUse CaseToken Type
Authorization Code + PKCEWeb apps, SPAs, mobile appsID token + access token
Client CredentialsDaemon services, background jobsAccess token (no user context)
On-Behalf-Of (OBO)API calling another APIDelegated access token
Device CodeCLI tools, IoT devicesID token + access token
Managed IdentityAzure resourcesAccess token (automatic)

Conditional Access Policies

Conditional Access is the core security feature of Entra ID P1 and above. It evaluates signals (who the user is, where they are, what device they are using, what application they are accessing) and enforces access decisions (allow, deny, require MFA, require compliant device, limit session). Conditional Access policies are the modern replacement for network-based security perimeters.

Policies follow an if-then pattern: if a specific set of conditions is met (assignments), then enforce specific controls (grant or session controls). Multiple policies can apply to a single sign-in, and all applicable policies must be satisfied.

bash
# List existing Conditional Access policies
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
  --query "value[].{Name:displayName, State:state, Created:createdDateTime}" -o table

# Create a policy: Require MFA for all users accessing Azure management
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
  --headers "Content-Type=application/json" \
  --body '{
    "displayName": "Require MFA for Azure Management",
    "state": "enabledForReportingButNotEnforced",
    "conditions": {
      "users": {
        "includeUsers": ["All"],
        "excludeUsers": [],
        "excludeGroups": ["break-glass-group-id"]
      },
      "applications": {
        "includeApplications": ["797f4846-ba00-4fd7-ba43-dac1f8f63013"]
      },
      "locations": {
        "includeLocations": ["All"],
        "excludeLocations": ["AllTrusted"]
      }
    },
    "grantControls": {
      "operator": "OR",
      "builtInControls": ["mfa"]
    }
  }'

# Create a policy: Block legacy authentication
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
  --headers "Content-Type=application/json" \
  --body '{
    "displayName": "Block Legacy Authentication",
    "state": "enabled",
    "conditions": {
      "users": {
        "includeUsers": ["All"]
      },
      "applications": {
        "includeApplications": ["All"]
      },
      "clientAppTypes": [
        "exchangeActiveSync",
        "other"
      ]
    },
    "grantControls": {
      "operator": "OR",
      "builtInControls": ["block"]
    }
  }'

Always Keep a Break-Glass Account

Before enabling Conditional Access policies, create at least two break-glass (emergency access) accounts that are excluded from all policies. These accounts should have Global Administrator role, use long complex passwords stored securely (e.g., in a physical safe), and should not use MFA (or use a different MFA method like FIDO2 keys stored separately). Monitor their sign-in activity with alerts. A misconfigured Conditional Access policy can lock out your entire organization.

Recommended Baseline Policies

PolicyTargetControl
Require MFA for adminsDirectory roles (Global Admin, etc.)Require MFA
Require MFA for all usersAll users (exclude break-glass)Require MFA
Block legacy authAll users, legacy client appsBlock access
Require compliant devicesAll users, corporate appsRequire compliant device
Block high-risk sign-insAll users (requires P2)Block or require MFA
Session limits for unmanaged devicesAll users, from non-compliant devicesApp enforced restrictions

Privileged Identity Management (PIM)

PIM is an Entra ID P2 feature that provides just-in-time (JIT) privileged access. Instead of permanently assigning powerful roles like Global Administrator or Subscription Owner, users are made eligible for the role. When they need elevated access, they activate the role for a limited duration (1-24 hours), optionally requiring approval and justification.

This dramatically reduces the attack surface because privileged credentials are not standing. Even if an attacker compromises a user's account, they do not automatically have admin access. They would need to activate the role, which triggers alerts and may require MFA and approval.

bash
# List PIM role assignments
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleRequests" \
  --query "value[].{Principal:principalId, Role:roleDefinitionId, Status:status}" -o table

# Create an eligible role assignment (user is eligible but not active)
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleRequests" \
  --headers "Content-Type=application/json" \
  --body '{
    "action": "adminAssign",
    "justification": "Security admin role needed for incident response duties",
    "roleDefinitionId": "194ae4cb-b126-40b2-bd5b-6091b380977d",
    "directoryScopeId": "/",
    "principalId": "user-object-id",
    "scheduleInfo": {
      "startDateTime": "2026-03-14T00:00:00Z",
      "expiration": {
        "type": "afterDuration",
        "duration": "P365D"
      }
    }
  }'

# Activate an eligible role (self-service elevation)
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests" \
  --headers "Content-Type=application/json" \
  --body '{
    "action": "selfActivate",
    "justification": "Investigating security alert SEC-2026-0442",
    "roleDefinitionId": "194ae4cb-b126-40b2-bd5b-6091b380977d",
    "directoryScopeId": "/",
    "principalId": "my-user-object-id",
    "scheduleInfo": {
      "startDateTime": "2026-03-14T09:00:00Z",
      "expiration": {
        "type": "afterDuration",
        "duration": "PT4H"
      }
    }
  }'

PIM for Azure Resources Too

PIM is not limited to Entra ID directory roles. You can also use PIM for Azure resource roles (Owner, Contributor, Reader on subscriptions, resource groups, and individual resources). This means developers can have eligible Contributor access to production subscriptions and only activate it when performing deployments, with full audit trails.

B2B Collaboration

Entra ID B2B (Business-to-Business) enables you to invite external users (partners, vendors, contractors) to access your applications and resources. External users authenticate with their own identity provider (their company's Entra ID, Google Workspace, or a one-time email passcode) and are represented as guest users in your tenant. You control their access through the same groups, roles, and Conditional Access policies as internal users.

bash
# Invite an external user
az ad user invite \
  --invited-user-email-address "partner@externalcompany.com" \
  --invited-user-display-name "Partner User" \
  --invite-redirect-url "https://portal.azure.com" \
  --send-invitation-message true

# Configure B2B cross-tenant access settings
az rest --method PATCH \
  --url "https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default" \
  --headers "Content-Type=application/json" \
  --body '{
    "b2bCollaborationInbound": {
      "usersAndGroups": {
        "accessType": "allowed",
        "targets": [{"target": "AllUsers", "targetType": "user"}]
      },
      "applications": {
        "accessType": "allowed",
        "targets": [{"target": "AllApplications", "targetType": "application"}]
      }
    }
  }'

# List guest users in your tenant
az ad user list \
  --filter "userType eq 'Guest'" \
  --query "[].{Name:displayName, Email:mail, Created:createdDateTime}" \
  -o table

B2C for Customer-Facing Applications

Entra ID B2C (Business-to-Consumer) is a separate service designed for customer-facing identity. While Entra ID handles your workforce (employees, partners), B2C handles your customers at massive scale (millions of users). B2C supports social identity providers (Google, Facebook, Apple), local accounts with email/password, and custom user flows for sign-up, sign-in, and profile editing.

B2C tenants are separate from your main Entra ID tenant. They have their own configuration, custom branding, and user flows. The pricing model is based on monthly active users (MAUs), with the first 50,000 MAUs free for MFA and Standard tier.

bash
# B2C is typically configured in the portal, but you can manage it via Graph API

# Register a B2C application
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/applications" \
  --headers "Content-Type=application/json" \
  --body '{
    "displayName": "Customer Portal",
    "signInAudience": "AzureADandPersonalMicrosoftAccount",
    "web": {
      "redirectUris": [
        "https://app.contoso.com/auth/callback",
        "https://localhost:3000/auth/callback"
      ],
      "implicitGrantSettings": {
        "enableIdTokenIssuance": true
      }
    },
    "requiredResourceAccess": [
      {
        "resourceAppId": "00000003-0000-0000-c000-000000000000",
        "resourceAccess": [
          {
            "id": "37f7f235-527c-4136-accd-4a02d197296e",
            "type": "Scope"
          }
        ]
      }
    ]
  }'

# Example: B2C user flow configuration in custom policy XML
# (Custom policies use the Identity Experience Framework)
# Deploy via: az rest --method PUT --url "...trustFrameworkPolicies/..."

# Query B2C users
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/users" \
  --headers "Content-Type=application/json" \
  --query "value[].{Name:displayName, Email:identities[0].issuerAssignedId, Provider:identities[0].signInType}"

Security Best Practices

Entra ID is your identity perimeter. A compromised identity is the starting point for most cloud breaches. Implement these security controls to protect your tenant.

Identity Security Checklist

ControlPriorityLicense Required
Enable MFA for all usersCriticalFree (Security Defaults) or P1 (CA)
Block legacy authenticationCriticalP1 (Conditional Access)
Create break-glass accountsCriticalFree
Enable PIM for privileged rolesHighP2
Configure Conditional AccessHighP1
Enable Identity ProtectionHighP2
Review guest user access quarterlyMediumP2 (Access Reviews)
Restrict app consent to adminsMediumFree
Enable sign-in risk policiesMediumP2
Monitor privileged role activationsMediumP2
bash
# Check current security defaults status
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy" \
  --query '{Enabled:isEnabled, Description:description}'

# Restrict user consent to admin-approved permissions only
az rest --method PATCH \
  --url "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" \
  --headers "Content-Type=application/json" \
  --body '{"defaultUserRolePermissions": {"permissionGrantPoliciesAssigned": []}}'

# Configure password protection (ban common passwords)
az rest --method PATCH \
  --url "https://graph.microsoft.com/v1.0/settings" \
  --headers "Content-Type=application/json" \
  --body '{
    "values": [
      {"name": "BannedPasswordCheckOnPremisesMode", "value": "Enforce"},
      {"name": "EnableBannedPasswordCheck", "value": "True"},
      {"name": "CustomBannedPasswords", "value": "contoso,password123,company2026"}
    ]
  }'

# Review sign-in logs for suspicious activity
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=riskLevelDuringSignIn eq 'high'&$top=25" \
  --query "value[].{User:userDisplayName, App:appDisplayName, Risk:riskLevelDuringSignIn, Location:location.city, Status:status.errorCode}"

# List users without MFA registered
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/reports/credentialUserRegistrationDetails?$filter=isMfaRegistered eq false" \
  --query "value[].{User:userDisplayName, UPN:userPrincipalName, MFA:isMfaRegistered}"

# Export Conditional Access policies for backup
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" > ca-policies-backup.json

Disable Security Defaults Before Using Conditional Access

Security Defaults and Conditional Access are mutually exclusive. Security Defaults provides a baseline set of security controls (MFA for all, block legacy auth) that are free and require no configuration. Once you create custom Conditional Access policies, disable Security Defaults first. If you skip this step, your Conditional Access policies may not apply correctly and you may get unexpected authentication behavior.

Monitoring and Diagnostics

Entra ID generates extensive logs that are essential for security monitoring and troubleshooting. Sign-in logs capture every authentication attempt with details about the user, device, location, application, and Conditional Access evaluation. Audit logs track administrative changes to the directory.

bash
# Stream Entra ID logs to Azure Monitor (Log Analytics workspace)
az monitor diagnostic-settings create \
  --name "entra-id-logs" \
  --resource "/providers/Microsoft.aadiam/diagnosticSettings" \
  --workspace "/subscriptions/sub-id/resourceGroups/rg-security/providers/Microsoft.OperationalInsights/workspaces/security-workspace" \
  --logs '[
    {"category": "SignInLogs", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}},
    {"category": "AuditLogs", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}},
    {"category": "NonInteractiveUserSignInLogs", "enabled": true},
    {"category": "ServicePrincipalSignInLogs", "enabled": true},
    {"category": "ManagedIdentitySignInLogs", "enabled": true},
    {"category": "RiskyUsers", "enabled": true},
    {"category": "UserRiskEvents", "enabled": true}
  ]'

# KQL query: Find failed sign-ins from unusual locations
# (Run in Log Analytics workspace)
# SigninLogs
# | where ResultType != 0
# | summarize FailedAttempts=count() by UserDisplayName, Location=tostring(LocationDetails.city), IPAddress
# | where FailedAttempts > 5
# | order by FailedAttempts desc

Next Steps

Entra ID is the foundation for securing your Azure environment. Once you have the basics in place (MFA, Conditional Access, PIM), explore these advanced scenarios:

Workload Identities: Manage identities for applications and services with Entra Workload ID, including federated credentials for GitHub Actions and Kubernetes service accounts.

Entra Verified ID: Issue and verify decentralized credentials for identity verification scenarios without sharing personal data.

Cross-tenant synchronization: Automatically provision and deprovision users across tenants in multi-tenant organizations.

Entra Internet Access: Replace traditional VPNs with identity-aware network access using Microsoft's Security Service Edge (SSE) solution.

Azure Landing Zones: CAF and GovernanceAI Services Across CloudsAzure OpenAI Service Guide

Key Takeaways

  1. 1Entra ID is fundamentally different from on-premises AD DS, using OAuth 2.0/OIDC instead of LDAP/Kerberos.
  2. 2App registrations define application identity; service principals are the local instance in a tenant.
  3. 3Conditional Access policies are the primary security mechanism for controlling sign-in behavior.
  4. 4PIM provides just-in-time privileged access, reducing the attack surface of standing permissions.
  5. 5Always maintain break-glass accounts excluded from all Conditional Access policies.
  6. 6Managed Identities should be preferred over app registrations with client secrets for Azure resources.

Frequently Asked Questions

What is the difference between Entra ID and Active Directory?
Entra ID (formerly Azure AD) is a cloud-based identity service using OAuth 2.0, OIDC, and SAML. On-premises Active Directory Domain Services (AD DS) uses LDAP, Kerberos, and Group Policy. They serve different purposes but can be synchronized using Entra Connect.
Do I need Entra ID P1 or P2?
P1 ($6/user/month) adds Conditional Access and group-based licensing. P2 ($9/user/month) adds PIM, Identity Protection, and access reviews. Most production environments need P1 for Conditional Access. P2 is recommended for organizations with privileged admin roles.
What are Managed Identities and when should I use them?
Managed Identities provide Azure resources with an automatically managed identity in Entra ID. Use them instead of app registrations for any code running on Azure (VMs, App Service, Functions, AKS). They eliminate credential management and provide automatic rotation.
How do I set up MFA for all users?
For simple setups, enable Security Defaults (free, forces MFA for all users). For granular control, create a Conditional Access policy targeting All Users (excluding break-glass accounts) with a grant control of 'Require MFA'. Disable Security Defaults before using Conditional Access.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.