In this module

IAM0.4 Non-Human Identity — The Governance Gap Nobody Sees

6 hours · Module 0 · Free
What you already know

IAM0.1 through IAM0.3 focused on human identities — member accounts, guest accounts, group memberships, permission creep, access reviews. This section turns to the identities most tenants don't govern at all: service principals, app registrations, managed identities, and the emerging AI agent model. These identities outnumber humans in most tenants, hold broader permissions, authenticate without MFA, and receive zero lifecycle governance. You'll run a non-human identity census against your own tenant and see the gap firsthand.

The identities nobody manages

Your tenant has 810 human identities. IAM0.1 through IAM0.3 measured their governance gaps — missing attributes, stale accounts, permission creep, rubber-stamp reviews. Those gaps are serious. But they exist within a lifecycle that at least has a creation event (someone creates the account) and a removal event (someone eventually disables it). Non-human identities often have neither.

A service principal is created when a developer registers an application or when an admin consents to a third-party app. A client secret is generated. API permissions are granted — sometimes User.Read.All, sometimes Mail.ReadWrite, sometimes Directory.ReadWrite.All. The integration works. The developer moves on. The service principal persists with its permissions and credentials, governed by nobody, reviewed by nobody, and invisible to the access review program that only evaluates human identities.

In most M365 tenants, non-human identities outnumber humans. Your tenant has 810 users. How many app registrations does it have? How many service principals? How many of those have credentials, and how many of those credentials have expired? You're about to find out.

Estimated time: 55 minutes.

NON-HUMAN IDENTITY LANDSCAPE HUMAN 810 members 23 guests Some governance exists (gaps measured in IAM0.1–0.3) APP REGISTRATIONS 347 registered 89 with credentials Zero governance SERVICE PRINCIPALS 1,200+ in tenant Most are Microsoft Zero governance MANAGED IDS Azure resources No stored secrets Partial governance AI AGENTS Entra Agent ID (Preview 2026) No governance THE GOVERNANCE ASYMMETRY Human identities: lifecycle exists (create → assign → sometimes review → sometimes remove) Non-human identities: created → credentials granted → permissions assigned → forgotten No access reviews. No periodic certification. No owner accountability. No credential rotation policy. Modules 9–11 build the governance framework that doesn't exist yet.

Figure IAM0.4 — The governance asymmetry. Human identities have at least a partial lifecycle. Non-human identities — app registrations, service principals, managed identities, and AI agents — are created, credentialed, permissioned, and forgotten. They outnumber humans and receive zero governance in most tenants.

Your non-human identity census

What we see in 90% of tenants (and why it fails)

Nobody owns the app registration lifecycle. A developer creates an app registration for a proof-of-concept, generates a client secret, grants API permissions, and moves on. The PoC ends. The app registration survives with its permissions and credentials. Six months later, the developer leaves. The app registration is now orphaned — active credentials, tenant-wide permissions, no owner, no review, no decommissioning date.

Entra Admin Center

IdentityApplicationsApp registrationsAll applications
This page lists every app registration in your tenant. Count the total. Click any app → Certificates & secrets to see its credentials — client secrets with expiry dates and certificates. Click API permissions to see what Graph API permissions the app has been granted. Click Owners to see who's accountable (often empty). The portal gives you a per-app view. The PowerShell census below gives you the tenant-wide picture.

Connect to Microsoft Graph with the scopes needed for application and service principal queries:

Connect-MgGraph -Scopes "Application.Read.All", "Directory.Read.All"

Start with app registrations — applications registered in your tenant:

$apps = Get-MgApplication -All -Property id, displayName, appId,
  createdDateTime, passwordCredentials, keyCredentials,
  requiredResourceAccess, signInAudience

Write-Host "=== APP REGISTRATION CENSUS ==="
Write-Host "Total app registrations: $($apps.Count)"

$withSecrets = ($apps | Where-Object { $_.PasswordCredentials.Count -gt 0 }).Count
$withCerts = ($apps | Where-Object { $_.KeyCredentials.Count -gt 0 }).Count
$withPerms = ($apps | Where-Object { $_.RequiredResourceAccess.Count -gt 0 }).Count
$noCredentials = ($apps | Where-Object {
  $_.PasswordCredentials.Count -eq 0 -and $_.KeyCredentials.Count -eq 0
}).Count

Write-Host "  With client secrets: $withSecrets"
Write-Host "  With certificates:   $withCerts"
Write-Host "  With API permissions: $withPerms"
Write-Host "  No credentials at all: $noCredentials"
=== APP REGISTRATION CENSUS ===
Total app registrations: 347
  With client secrets: 78
  With certificates:   11
  With API permissions: 289
  No credentials at all: 258

Three hundred and forty-seven app registrations. Compare that to your 810 human identities. In many production tenants the ratio is worse — organizations with 500 employees routinely have 400+ app registrations because every SaaS integration, every developer project, every proof-of-concept, and every Azure resource that needs an identity creates one.

78 registrations have client secrets — stored passwords that authenticate the application. 11 have certificates. 258 have no credentials, which means they're either unused, authenticate through a different mechanism (federated credentials, managed identity), or were created and abandoned. 289 have API permissions requested, which means they've declared what access they need — though declared permissions and granted permissions aren't always the same thing.

Now query service principals — the tenant-side representation of applications. Every app registration creates a service principal in the home tenant. Third-party apps that users consent to also create service principals without a corresponding app registration in your tenant:

$sps = Get-MgServicePrincipal -All -Property id, displayName, appId,
  servicePrincipalType, appOwnerOrganizationId, createdDateTime,
  signInActivity

$firstParty = ($sps | Where-Object {
  $_.AppOwnerOrganizationId -eq "f8cdef31-a31e-4b4a-93e4-5f571e91255a"
}).Count
$yourOrg = ($sps | Where-Object {
  $_.ServicePrincipalType -eq "Application" -and
  $_.AppOwnerOrganizationId -ne "f8cdef31-a31e-4b4a-93e4-5f571e91255a"
}).Count
$thirdParty = ($sps | Where-Object {
  $_.ServicePrincipalType -eq "Application" -and
  $_.AppOwnerOrganizationId -and
  $_.AppOwnerOrganizationId -ne "f8cdef31-a31e-4b4a-93e4-5f571e91255a" -and
  $_.AppOwnerOrganizationId -ne (Get-MgOrganization).Id
}).Count

Write-Host "`n=== SERVICE PRINCIPAL CENSUS ==="
Write-Host "Total service principals: $($sps.Count)"
Write-Host "  Microsoft first-party: $firstParty"
Write-Host "  Your organization:     $yourOrg"
Write-Host "  Third-party vendors:   $thirdParty"
=== SERVICE PRINCIPAL CENSUS ===
Total service principals: 1,247
  Microsoft first-party: 891
  Your organization:     198
  Third-party vendors:   158

1,247 service principals. The majority (891) are Microsoft first-party — created automatically when you enable M365 services. You don't manage their credentials and Microsoft handles their lifecycle. The governance concern is with the other 356: 198 from your organization's app registrations and 158 from third-party vendors whose apps your users or admins consented to.

Those 158 third-party service principals are the ones most organizations can't account for. Each one was created when someone clicked "Accept" on an OAuth consent prompt. The service principal received whatever permissions the consent prompt requested. Nobody tracked which user consented, whether the permissions were appropriate, or whether the application still needs access.

At Northgate Engineering: Rachel Okafor runs the service principal census and finds 1,247 total. She can account for the Microsoft first-party apps. She recognizes about 60 of the 198 organizational apps — the ones her team built or uses daily. The other 138 organizational apps and all 158 third-party apps are unknown to her. When she asks Phil Greaves, he recognizes some from vendor evaluations and proof-of-concept projects that ended months ago. The service principals survived. Their permissions survived. Nobody decommissioned them because nobody owns the lifecycle.

Credential age — the secrets nobody rotates

App registrations authenticate using client secrets (passwords) or certificates. Both expire. When a credential expires, one of two things happens: someone rotates it (creates a new credential, updates the application, removes the old one) or nobody notices until the integration breaks at 2 AM on a Saturday.

Entra Admin Center

IdentityApplicationsApp registrations → select any app → Certificates & secrets
The Client secrets tab shows each secret's description, expiry date, and status. A red expiry date means the secret has expired. The Certificates tab shows the same for certificate credentials. Check 3–4 app registrations — particularly older ones. Count how many have expired secrets. The portal shows credential health per app. The PowerShell query below gives you the tenant-wide picture.

Query the credential health of your app registrations:

$now = Get-Date

$credentialHealth = $apps | Where-Object {
  $_.PasswordCredentials.Count -gt 0 -or $_.KeyCredentials.Count -gt 0
} | ForEach-Object {
  $secrets = $_.PasswordCredentials | ForEach-Object {
    $daysToExpiry = ($_.EndDateTime - $now).Days
    [PSCustomObject]@{
      Type   = "Secret"
      Expiry = $_.EndDateTime.ToString("yyyy-MM-dd")
      Days   = $daysToExpiry
      Status = if ($daysToExpiry -lt 0) { "EXPIRED" }
               elseif ($daysToExpiry -lt 30) { "CRITICAL" }
               elseif ($daysToExpiry -lt 90) { "WARNING" }
               else { "OK" }
    }
  }
  $certs = $_.KeyCredentials | ForEach-Object {
    $daysToExpiry = ($_.EndDateTime - $now).Days
    [PSCustomObject]@{
      Type   = "Certificate"
      Expiry = $_.EndDateTime.ToString("yyyy-MM-dd")
      Days   = $daysToExpiry
      Status = if ($daysToExpiry -lt 0) { "EXPIRED" }
               elseif ($daysToExpiry -lt 30) { "CRITICAL" }
               elseif ($daysToExpiry -lt 90) { "WARNING" }
               else { "OK" }
    }
  }
  $allCreds = @($secrets) + @($certs) | Where-Object { $_ }
  $worst = ($allCreds | Sort-Object Days | Select-Object -First 1).Status

  [PSCustomObject]@{
    App           = $_.DisplayName
    Credentials   = $allCreds.Count
    WorstStatus   = $worst
    NearestExpiry = ($allCreds | Sort-Object Days | Select-Object -First 1).Expiry
  }
}

$expired = ($credentialHealth | Where-Object { $_.WorstStatus -eq "EXPIRED" }).Count
$critical = ($credentialHealth | Where-Object { $_.WorstStatus -eq "CRITICAL" }).Count
$warning = ($credentialHealth | Where-Object { $_.WorstStatus -eq "WARNING" }).Count

Write-Host "=== CREDENTIAL HEALTH ==="
Write-Host "Apps with credentials: $($credentialHealth.Count)"
Write-Host "  EXPIRED:  $expired"
Write-Host "  CRITICAL (< 30 days): $critical"
Write-Host "  WARNING  (< 90 days): $warning"

$credentialHealth | Where-Object { $_.WorstStatus -in "EXPIRED", "CRITICAL" } |
  Sort-Object NearestExpiry |
  Format-Table App, Credentials, WorstStatus, NearestExpiry -AutoSize
=== CREDENTIAL HEALTH ===
Apps with credentials: 89
  EXPIRED:  31
  CRITICAL (< 30 days): 7
  WARNING  (< 90 days): 12

App                        Credentials  WorstStatus  NearestExpiry
---                        -----------  -----------  -------------
Legacy CRM Connector       2            EXPIRED      2025-03-14
Vendor Reporting Tool      1            EXPIRED      2025-06-20
svc-data-pipeline          1            EXPIRED      2025-08-01
Dev Test App               1            EXPIRED      2025-09-12
...

Thirty-one app registrations with expired credentials. Those credentials can no longer authenticate — but the app registrations still exist, the permissions are still granted, and someone could generate a new secret and immediately gain the access the original developer had. Seven more are within 30 days of expiry. Nobody is monitoring expiry dates because no credential governance process exists.

The expired credentials tell a governance story. Legacy CRM Connector expired in March 2025 — over a year ago. If the integration still worked, someone created a new credential outside the app registration (perhaps hardcoded in a script). If it stopped working, either nobody noticed or someone fixed it without cleaning up the expired credential. Either outcome is a governance failure.

Permission scope — what non-human identities can access

Credentials get the identity in the door. Permissions determine what it can do once inside. Query the API permissions granted to your organization's app registrations:

$graphAppId = "00000003-0000-0000-c000-000000000000"  # Microsoft Graph

$permissionAudit = $apps | Where-Object {
  $_.RequiredResourceAccess | Where-Object { $_.ResourceAppId -eq $graphAppId }
} | ForEach-Object {
  $graphPerms = ($_.RequiredResourceAccess |
    Where-Object { $_.ResourceAppId -eq $graphAppId }).ResourceAccess

  $appPerms = ($graphPerms | Where-Object { $_.Type -eq "Role" }).Count
  $delegatedPerms = ($graphPerms | Where-Object { $_.Type -eq "Scope" }).Count

  [PSCustomObject]@{
    App            = $_.DisplayName
    AppPermissions = $appPerms
    Delegated      = $delegatedPerms
    Total          = $appPerms + $delegatedPerms
  }
} | Sort-Object AppPermissions -Descending

Write-Host "=== GRAPH API PERMISSION AUDIT ==="
Write-Host "Apps requesting Graph permissions: $($permissionAudit.Count)"
$permissionAudit | Select-Object -First 10 | Format-Table -AutoSize
=== GRAPH API PERMISSION AUDIT ===
Apps requesting Graph permissions: 167

App                        AppPermissions  Delegated  Total
---                        --------------  ---------  -----
Data Migration Tool        12              0          12
svc-user-provisioning      8               0          8
Vendor SSO Connector       6               3          9
Legacy Reporting           5               2          7
Dev Test App               4               0          4
...

AppPermissions (application permissions, also called "Role") are the dangerous ones. Delegated permissions act on behalf of a signed-in user and are constrained by that user's access. Application permissions act as the application itself — no user context, no user-level constraints. An app with Mail.ReadWrite as an application permission can read every mailbox in the tenant. An app with Directory.ReadWrite.All can modify any object in Entra ID.

Data Migration Tool holds 12 application permissions. Was that a one-time migration that completed months ago? Are those permissions still needed? Is the tool still running? The app registration doesn't tell you. The credential may be expired (check the credential health output). The permissions persist regardless of credential state — they're ready to use the moment someone generates a new secret.

Now identify the highest-risk permissions — the ones that grant tenant-wide read or write access:

$highRiskPerms = @(
  "User.ReadWrite.All", "Directory.ReadWrite.All", "Mail.ReadWrite",
  "Mail.Read", "Files.ReadWrite.All", "Sites.ReadWrite.All",
  "Group.ReadWrite.All", "RoleManagement.ReadWrite.Directory"
)

$graphSP = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$graphRoles = $graphSP.AppRoles

$apps | ForEach-Object {
  $graphAccess = $_.RequiredResourceAccess |
    Where-Object { $_.ResourceAppId -eq $graphAppId }
  if ($graphAccess) {
    $appRoles = $graphAccess.ResourceAccess |
      Where-Object { $_.Type -eq "Role" }
    $roleNames = $appRoles | ForEach-Object {
      $roleId = $_.Id
      ($graphRoles | Where-Object { $_.Id -eq $roleId }).Value
    }
    $risky = $roleNames | Where-Object { $_ -in $highRiskPerms }
    if ($risky) {
      [PSCustomObject]@{
        App       = $_.DisplayName
        HighRisk  = ($risky -join ", ")
        Count     = $risky.Count
      }
    }
  }
} | Sort-Object Count -Descending | Format-Table -AutoSize
App                        HighRisk                                          Count
---                        --------                                          -----
Data Migration Tool        User.ReadWrite.All, Group.ReadWrite.All, ...      5
svc-user-provisioning      User.ReadWrite.All, Group.ReadWrite.All           2
Vendor Reporting           Mail.Read, Sites.ReadWrite.All                    2
Legacy Reporting           Files.ReadWrite.All                               1

Each of these apps can do exactly what the permission name says — at the tenant level, without a user context, without MFA. Mail.Read as an application permission means the app can read every user's mailbox. Not one user's mailbox. Every mailbox. That's the permission a developer requested during a proof of concept and nobody scoped down afterward.

Before you move on, apply the three diagnostic questions from IAM0.1 to one of your high-permission app registrations. Why does this app have these permissions? Was the permission scope deliberately chosen, or did the developer request everything the API offered? When were these permissions last reviewed? Has anyone verified that the app still needs Directory.ReadWrite.All since the migration completed? Who is accountable for this app registration? If the developer who created it left the organization, who owns the lifecycle now?

At Northgate Engineering: Rachel Okafor runs the permission audit and finds Data Migration Tool with 12 application permissions including User.ReadWrite.All, Group.ReadWrite.All, and Mail.ReadWrite. The migration completed 11 months ago. The app registration still exists. The credentials expired 6 months ago — but the permissions remain. She asks Phil Greaves who owns it. He says the contractor who built the migration tool left 8 months ago. Nobody reassigned ownership. Nobody revoked the permissions. The app registration sits in the tenant like an unlocked service entrance — the key doesn't currently work (expired credential), but the door is still propped open (permissions still granted), and cutting a new key takes 30 seconds (generate a new client secret).

The AI agent frontier

Beyond service principals and managed identities, a new category of non-human identity is emerging. Microsoft launched Entra Agent ID in preview in March 2026, alongside Agent 365 licensing. AI agents — Copilot agents, custom agents built on Azure AI, and third-party agents — now have their own identity model in Entra ID.

Agent identities differ from service principals in ways that change the governance model. An agent acts autonomously — it makes decisions about which data to access based on its instructions, not a predefined API call. An agent's permission scope is harder to predict because the agent decides at runtime which Graph API endpoints to call. An agent can be granted permissions through entitlement management, but the agent's actual data access patterns depend on its instructions, the user's context, and the model's reasoning — not a static configuration.

The governance questions for AI agents are the same three diagnostic questions, plus a fourth: What can this agent actually do with its permissions? A service principal with Mail.ReadWrite reads and writes mail in a predictable pattern defined by its code. An AI agent with the same permission reads and writes mail based on instructions it receives from users, other agents, or its own reasoning chain. The permission is the same. The risk surface is different.

Module 11 builds the governance framework for AI agent identities — registration governance, blueprint-based policy templates, sponsor accountability, permission boundaries, and lifecycle management. The framework doesn't exist yet in most organizations because the identity model is new. You'll build it.

If your organization already uses Copilot agents, custom AI agents, or is evaluating third-party agent platforms, the governance problem is already present — even if Entra Agent ID isn't fully deployed. Those agents authenticate through service principals or user-delegated tokens today. They appear in your service principal census alongside every other application. The difference is that their access patterns are unpredictable by design — the model decides what to query at runtime, not a developer at build time. A service principal running a nightly data sync touches the same endpoints every execution. An AI agent responding to user prompts touches different endpoints every time, limited only by its granted permissions. The governance implication: you can't monitor agent behavior by comparing it to a known baseline. You need permission boundaries that constrain what the agent can do, not just logging that records what it did.

The governance gap summarized

Run the full non-human identity count against your human identity count:

$humanCount = (Get-MgUser -All | Where-Object { $_.UserType -eq "Member" }).Count
$guestCount = (Get-MgUser -All | Where-Object { $_.UserType -eq "Guest" }).Count
$appCount = (Get-MgApplication -All).Count
$spCount = (Get-MgServicePrincipal -All).Count

Write-Host "=== IDENTITY RATIO ==="
Write-Host "Human members:      $humanCount"
Write-Host "Guests:             $guestCount"
Write-Host "App registrations:  $appCount"
Write-Host "Service principals: $spCount"
Write-Host "Non-human total:    $($appCount + $spCount)"
Write-Host "Ratio:              $([math]::Round(($appCount + $spCount) / $humanCount, 1)):1 non-human to human"
=== IDENTITY RATIO ===
Human members:      810
Guests:             23
App registrations:  347
Service principals: 1247
Non-human total:    1594
Ratio:              2.0:1 non-human to human

Two non-human identities for every human. The human identities have at least a partial lifecycle — creation, some assignment governance, maybe an annual review. The non-human identities have creation and nothing else. No access reviews. No periodic certification. No owner accountability. No credential rotation policy. No lifecycle automation.

That asymmetry is the governance gap Modules 9 through 11 close. By Module 11, every non-human identity type — service principals, managed identities, and AI agents — has a governance framework with inventory, classification, credential policies, permission right-sizing, owner accountability, and review cadences.


Reusable script — the non-human identity census from this section:

# IAM0.4 — Non-Human Identity Census
Connect-MgGraph -Scopes "Application.Read.All", "Directory.Read.All"

# App registration census
$apps = Get-MgApplication -All -Property id, displayName, appId,
  createdDateTime, passwordCredentials, keyCredentials, requiredResourceAccess

Write-Host "=== APP REGISTRATION CENSUS ==="
Write-Host "Total: $($apps.Count)"
Write-Host "  With secrets:      $(($apps | Where-Object { $_.PasswordCredentials.Count -gt 0 }).Count)"
Write-Host "  With certificates: $(($apps | Where-Object { $_.KeyCredentials.Count -gt 0 }).Count)"
Write-Host "  With permissions:  $(($apps | Where-Object { $_.RequiredResourceAccess.Count -gt 0 }).Count)"

# Credential health
$now = Get-Date
$expired = 0; $critical = 0; $warning = 0
$apps | Where-Object { $_.PasswordCredentials.Count -gt 0 -or $_.KeyCredentials.Count -gt 0 } |
  ForEach-Object {
    $allCreds = @($_.PasswordCredentials) + @($_.KeyCredentials)
    $allCreds | ForEach-Object {
      $days = ($_.EndDateTime - $now).Days
      if ($days -lt 0) { $script:expired++ }
      elseif ($days -lt 30) { $script:critical++ }
      elseif ($days -lt 90) { $script:warning++ }
    }
  }

Write-Host "`n=== CREDENTIAL HEALTH ==="
Write-Host "  Expired credentials:       $expired"
Write-Host "  Critical (< 30 days):      $critical"
Write-Host "  Warning (< 90 days):       $warning"

# Service principal census
$sps = Get-MgServicePrincipal -All -Property id, displayName,
  servicePrincipalType, appOwnerOrganizationId
$msft = ($sps | Where-Object {
  $_.AppOwnerOrganizationId -eq "f8cdef31-a31e-4b4a-93e4-5f571e91255a"
}).Count

Write-Host "`n=== SERVICE PRINCIPAL CENSUS ==="
Write-Host "Total: $($sps.Count)"
Write-Host "  Microsoft first-party: $msft"
Write-Host "  Other:                 $($sps.Count - $msft)"

# Identity ratio
$humanCount = (Get-MgUser -All | Where-Object { $_.UserType -eq "Member" }).Count
Write-Host "`n=== IDENTITY RATIO ==="
Write-Host "Human: $humanCount | Non-human: $($apps.Count + $sps.Count)"
Write-Host "Ratio: $([math]::Round(($apps.Count + $sps.Count) / $humanCount, 1)):1"

Decision-point simulation

Scenario 1. Your app registration audit reveals 347 registrations. 89 have active credentials. 31 of those 89 have credentials that expired more than 6 months ago but the app registration still exists. What's the governance risk of expired credentials on active app registrations?

Expired credentials mean the app can't authenticate — but the permissions are still granted. If someone creates a new credential on that app registration (and anyone with Application Administrator or the app's owner role can do this), the app immediately regains all its previously granted permissions. The 31 registrations with expired credentials are dormant attack surfaces: the permissions persist silently, waiting for a new credential. The remediation: review all 31 with their owners. If the app is no longer needed, delete the registration (which removes the service principal and all permissions). If the app is needed, rotate the credential and document the lifecycle.

Scenario 2. A developer creates an app registration for a CI/CD pipeline and grants it Directory.ReadWrite.All — the broadest directory permission available. The app works. The developer moves to another team. Nobody else knows the app exists. What governance controls would have prevented this?

Three controls: consent policy (admin consent required for high-privilege permissions like Directory.ReadWrite.All), app registration ownership governance (every registration must have at least two owners), and credential expiry enforcement (maximum 6-month credential lifetime forces periodic review). Without these, the app becomes an orphaned identity with domain-admin-equivalent permissions and no accountable owner. Module 7 builds the service principal governance framework that prevents this pattern.

Scenario 3. Your tenant has 15 managed identities (system-assigned) on Azure resources. Nobody has inventoried what permissions they hold. Your colleague argues that managed identities are "safe" because they have no credentials to leak. Is that correct?

Partially. Managed identities eliminate credential leakage — there's no secret or certificate to steal. But they still hold RBAC permissions, and over-provisioned managed identities are just as dangerous as over-provisioned service principals. A managed identity with Contributor on a subscription can modify any resource in that subscription. The "no credentials" argument addresses one threat vector (credential theft) but ignores another (permission scope). Module 8 covers managed identity permission auditing and right-sizing.

Next

IAM0.5 — The NE Scenario. You've audited your own tenant across four subs — identity census, lifecycle stages, governance principles, and non-human identity. IAM0.5 introduces the Northgate Engineering environment you'll use as the worked example throughout the course: 810 staff, 347 app registrations, 214 groups, 23 guest accounts, 8 governance gaps mapped to the modules that fix them, and the named personas you'll encounter from Module 1 onward.

You're reading the free modules of Identity and Access Management in Microsoft 365

The full course continues with advanced topics, production detection rules, worked investigation scenarios, and deployable artifacts.

View Pricing See Full Syllabus