In this module

IAM0.6 Lab Setup and Cost Management

6 hours · Module 0 · Free
What you already know

IAM0.1 through IAM0.4 audited your own tenant. IAM0.5 introduced the Northgate Engineering scenario with its 8 governance gaps. This section builds the NE lab environment in your M365 developer tenant — 15 persona accounts, groups with deliberate governance gaps, app registrations with varying credential states, guest accounts, and the Entra ID Governance trial license that unlocks lifecycle workflows, entitlement management, and access reviews from Module 2 onward.

What you need before starting

Two things: an M365 E5 developer tenant and a connection to Microsoft Graph PowerShell. If you already have both from an earlier Ridgeline course or your own lab environment, skip to the Governance trial activation section.

Lab Environment — Components M365 E5 Tenant Developer Program 25 E5 licenses Entra ID P2 included Governance Trial 90-day free trial Lifecycle workflows Entitlement mgmt NE Personas 15 test accounts 6 departments Governance attrs set Test Objects 7 app registrations 5 guest accounts 13 groups Total estimated cost: $0 (developer tenant + trial). Azure costs minimal if needed.

Estimated time: 45 minutes (first setup) or 25 minutes (existing tenant).

M365 E5 developer tenant

The Microsoft 365 Developer Program provides a free E5 tenant with 25 user licenses. The tenant is non-production — designed for development and learning. It includes everything this course requires: Entra ID P2, Exchange Online, SharePoint Online, Teams, Defender XDR, and Sentinel.

Microsoft Developer Program

Sign up: developer.microsoft.com/microsoft-365/dev-program
Sign in with a Microsoft account (personal or work). Select Set up E5 subscription. Choose a country, create an admin username and domain name (e.g., admin@yourlab.onmicrosoft.com). The tenant provisions in 2–3 minutes.

Verify: Sign in to entra.microsoft.com with your new admin account. Navigate to IdentityOverview. Confirm you see your tenant name and that the license shows Microsoft 365 E5 Developer.

The developer tenant renews automatically as long as you're actively developing — creating users, configuring policies, running PowerShell commands. The lab work in this course constitutes active development.

Connect to Microsoft Graph PowerShell

Install the Microsoft Graph PowerShell SDK if you haven't already:

Install-Module Microsoft.Graph -Scope CurrentUser -Force

Connect with the scopes needed for this setup:

Connect-MgGraph -Scopes "User.ReadWrite.All", "Group.ReadWrite.All",
  "Application.ReadWrite.All", "Directory.ReadWrite.All",
  "RoleManagement.ReadWrite.Directory"

A browser consent prompt appears. Sign in with the admin account from your developer tenant and grant the permissions. Verify the connection:

Get-MgContext | Select-Object Account, TenantId, Scopes

You should see your admin UPN, the tenant ID, and the granted scopes. If any scope is missing, disconnect and reconnect with the full scope list.

Activate the Entra ID Governance trial

The E5 developer license includes Entra ID P2 — Conditional Access, Identity Protection, and basic PIM. It does not include Entra ID Governance, which provides lifecycle workflows, entitlement management, and access reviews. The course requires these from Module 2 onward.

Entra Admin Center

IdentityOverviewManage trials (or BillingPurchase services in the M365 admin center)
Search for Microsoft Entra ID Governance. Select the trial. It provides 90 days of full Governance functionality for up to 100 users. The trial is renewable — as long as you're actively using the features, Microsoft typically extends it.

Verify: After activation, navigate to Identity Governance in the Entra admin center sidebar. You should see Entitlement management, Access reviews, and Lifecycle workflows as available menu items. If these show "Requires license," the trial hasn't propagated yet — wait 10 minutes and refresh.

If the Governance trial isn't available in your region or has been previously used on this tenant, the course includes "Without Governance Licensing" callouts in every module where the feature is gated. You can complete Modules 0 and 1 without the Governance license. From Module 2 onward, the callouts provide Graph API and PowerShell workarounds for governance features that require the license.

Create the NE persona accounts

The following script creates 15 accounts representing NE personas across departments. The accounts are designed with deliberate governance gaps — some have complete attributes (department, manager, hire date), some are missing one or more. This mirrors the governance attribute coverage gaps you found in IAM0.1.

Entra Admin Center — Manual Alternative

IdentityUsersAll usersNew userCreate new user
If you prefer creating accounts through the portal: enter the Display name, User principal name, and generate a password. Under Properties, expand Job info to set Department, Job title, and Employee hire date for personas that should have these attributes. Leave them blank for the "gap" personas (James Whitfield, Sarah Blackwood, Mark Taylor, etc.) to reproduce the data quality findings from IAM0.1. The PowerShell script below creates all 15 accounts in one run — use whichever method you prefer.

Replace yourtenant.onmicrosoft.com with your developer tenant domain throughout.

$domain = "yourtenant.onmicrosoft.com"
$passwordProfile = @{
  Password                      = "IAMcourse2026!"
  ForceChangePasswordNextSignIn = $false
}

# Personas with full governance attributes
$fullPersonas = @(
  @{ First="Rachel"; Last="Okafor"; Dept="Security"; Title="CISO";
     Hire="2024-11-15" },
  @{ First="Tom"; Last="Ashworth"; Dept="Security"; Title="SOC Analyst L1";
     Hire="2023-08-01" },
  @{ First="Priya"; Last="Sharma"; Dept="Security"; Title="SOC Analyst L1";
     Hire="2022-06-15" },
  @{ First="Marcus"; Last="Webb"; Dept="Security"; Title="Security Architect";
     Hire="2023-01-10" },
  @{ First="Elena"; Last="Petrova"; Dept="GRC"; Title="GRC Analyst";
     Hire="2024-03-01" },
  @{ First="Phil"; Last="Greaves"; Dept="IT"; Title="IT Director";
     Hire="2022-03-01" }
)

# Personas with missing attributes (deliberate governance gaps)
$gapPersonas = @(
  @{ First="James"; Last="Whitfield"; Dept="Engineering"; Title="Engineer";
     Hire=$null },
  @{ First="Sarah"; Last="Blackwood"; Dept=$null; Title="Marketing Lead";
     Hire=$null },
  @{ First="David"; Last="Chen"; Dept="Engineering"; Title="Senior Engineer";
     Hire="2021-09-01" },
  @{ First="Lisa"; Last="Okonkwo"; Dept="Finance"; Title="Finance Analyst";
     Hire=$null },
  @{ First="Mark"; Last="Taylor"; Dept=$null; Title=$null;
     Hire=$null },
  @{ First="Anna"; Last="Kowalski"; Dept="HR"; Title="HR Manager";
     Hire="2023-06-01" },
  @{ First="Ben"; Last="Hughes"; Dept="Engineering"; Title="Project Lead";
     Hire=$null },
  @{ First="Fatima"; Last="Al-Rashid"; Dept="Legal"; Title="Legal Counsel";
     Hire="2024-01-15" },
  @{ First="Chris"; Last="Morrison"; Dept="IT"; Title="Systems Admin";
     Hire=$null }
)

$allPersonas = $fullPersonas + $gapPersonas

foreach ($p in $allPersonas) {
  $upn = "$($p.First.ToLower()).$($p.Last.ToLower())@$domain"
  $params = @{
    DisplayName       = "$($p.First) $($p.Last)"
    UserPrincipalName = $upn
    MailNickname      = "$($p.First.ToLower()).$($p.Last.ToLower())"
    AccountEnabled    = $true
    PasswordProfile   = $passwordProfile
  }
  if ($p.Dept) { $params.Department = $p.Dept }
  if ($p.Title) { $params.JobTitle = $p.Title }
  if ($p.Hire) { $params.EmployeeHireDate = $p.Hire }

  $user = New-MgUser @params
  Write-Host "Created: $($user.DisplayName) ($upn)"
}

Write-Host "`nCreated $($allPersonas.Count) personas"

After the script completes, verify the attribute coverage gaps:

$personas = Get-MgUser -Filter "startsWith(userPrincipalName, 'rachel.') or
  startsWith(userPrincipalName, 'tom.') or startsWith(userPrincipalName, 'priya.') or
  startsWith(userPrincipalName, 'marcus.') or startsWith(userPrincipalName, 'elena.') or
  startsWith(userPrincipalName, 'phil.') or startsWith(userPrincipalName, 'james.') or
  startsWith(userPrincipalName, 'sarah.') or startsWith(userPrincipalName, 'david.') or
  startsWith(userPrincipalName, 'lisa.') or startsWith(userPrincipalName, 'mark.') or
  startsWith(userPrincipalName, 'anna.') or startsWith(userPrincipalName, 'ben.') or
  startsWith(userPrincipalName, 'fatima.') or startsWith(userPrincipalName, 'chris.')" `
  -Property displayName, department, jobTitle, employeeHireDate

$personas | Select-Object displayName, department, jobTitle,
  @{N='HireDate'; E={ if ($_.EmployeeHireDate) { "Set" } else { "MISSING" }}} |
  Format-Table -AutoSize

You should see 6 personas with complete attributes and 9 with varying gaps — matching the 23% missing department and 80% missing hire date patterns from IAM0.1.

Set up manager relationships

Manager assignments drive access reviews (manager-based reviewers) and lifecycle workflows (manager notification). Set up the reporting structure:

# Get user IDs
$rachel = (Get-MgUser -Filter "displayName eq 'Rachel Okafor'").Id
$tom = (Get-MgUser -Filter "displayName eq 'Tom Ashworth'").Id
$phil = (Get-MgUser -Filter "displayName eq 'Phil Greaves'").Id

# Security team reports to Rachel
@("Tom Ashworth", "Priya Sharma", "Marcus Webb") | ForEach-Object {
  $user = Get-MgUser -Filter "displayName eq '$_'"
  Set-MgUserManagerByRef -UserId $user.Id -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/users/$rachel"
  }
  Write-Host "Set manager: $_ → Rachel Okafor"
}

# Elena reports to Rachel
$elena = Get-MgUser -Filter "displayName eq 'Elena Petrova'"
Set-MgUserManagerByRef -UserId $elena.Id -BodyParameter @{
  "@odata.id" = "https://graph.microsoft.com/v1.0/users/$rachel"
}
Write-Host "Set manager: Elena Petrova → Rachel Okafor"

# IT team reports to Phil
@("Chris Morrison") | ForEach-Object {
  $user = Get-MgUser -Filter "displayName eq '$_'"
  Set-MgUserManagerByRef -UserId $user.Id -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/users/$phil"
  }
  Write-Host "Set manager: $_ → Phil Greaves"
}

# Deliberately leave some personas without managers
Write-Host "`nPersonas without managers (deliberate gap):"
Write-Host "  Mark Taylor, Sarah Blackwood, Ben Hughes"

Create NE groups

The groups simulate NE's group landscape — some governed, some ownerless, some from completed projects:

$groups = @(
  @{ Name="SG-All-Staff"; Type="Security"; Desc="All NE staff"; Owner=$phil },
  @{ Name="SG-Engineering"; Type="Security"; Desc="Engineering department"; Owner=$null },
  @{ Name="SG-Finance-Readers"; Type="Security"; Desc=$null; Owner=$null },
  @{ Name="SG-SOC-Operations"; Type="Security"; Desc="SOC team operational access"; Owner=$rachel },
  @{ Name="SG-IT-Admins"; Type="Security"; Desc="IT administration"; Owner=$phil },
  @{ Name="SG-Engineering-Leads"; Type="Security"; Desc="Engineering team leads"; Owner=$null },
  @{ Name="SG-SharePoint-HR-Site"; Type="Security"; Desc=$null; Owner=$null },
  @{ Name="SG-Defender-Alerts"; Type="Security"; Desc="Defender alert recipients"; Owner=$rachel },
  @{ Name="SG-Compliance-Training"; Type="Security"; Desc=$null; Owner=$null },
  @{ Name="SG-Project-Alpha"; Type="Security"; Desc=$null; Owner=$null },
  @{ Name="Team-Office-Migration-2023"; Type="M365"; Desc=$null; Owner=$null },
  @{ Name="Team-SOC-L1"; Type="M365"; Desc="SOC Level 1 analyst team"; Owner=$tom },
  @{ Name="Team-Vendor-Onboarding-2024"; Type="M365"; Desc=$null; Owner=$null }
)

foreach ($g in $groups) {
  $params = @{
    DisplayName     = $g.Name
    MailEnabled     = ($g.Type -eq "M365")
    MailNickname    = $g.Name.ToLower() -replace '[^a-z0-9]', ''
    SecurityEnabled = $true
  }
  if ($g.Type -eq "M365") {
    $params.GroupTypes = @("Unified")
  }
  if ($g.Desc) { $params.Description = $g.Desc }

  $newGroup = New-MgGroup @params
  Write-Host "Created group: $($g.Name) ($($g.Type)) — Owner: $(if ($g.Owner) { 'Set' } else { 'NONE' })"

  if ($g.Owner) {
    New-MgGroupOwner -GroupId $newGroup.Id -BodyParameter @{
      "@odata.id" = "https://graph.microsoft.com/v1.0/users/$($g.Owner)"
    }
  }
}

Now add persona memberships that simulate the accumulation pattern — Priya holds finance access from her previous role, project groups from completed initiatives, and her current SOC groups:

# Priya's accumulated memberships (the mover problem)
$priya = (Get-MgUser -Filter "displayName eq 'Priya Sharma'").Id
@("SG-All-Staff", "SG-Engineering", "SG-Finance-Readers", "SG-Project-Alpha",
  "Team-Office-Migration-2023", "Team-SOC-L1", "SG-SOC-Operations",
  "SG-SharePoint-HR-Site", "SG-Defender-Alerts", "SG-Engineering-Leads",
  "SG-Compliance-Training") | ForEach-Object {
  $gid = (Get-MgGroup -Filter "displayName eq '$_'").Id
  New-MgGroupMember -GroupId $gid -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$priya"
  } -ErrorAction SilentlyContinue
}
Write-Host "Priya Sharma: 11 group memberships (mover problem)"

# Tom — SOC groups only
$tomId = (Get-MgUser -Filter "displayName eq 'Tom Ashworth'").Id
@("SG-All-Staff", "Team-SOC-L1", "SG-SOC-Operations", "SG-Defender-Alerts") | ForEach-Object {
  $gid = (Get-MgGroup -Filter "displayName eq '$_'").Id
  New-MgGroupMember -GroupId $gid -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$tomId"
  } -ErrorAction SilentlyContinue
}

# Phil — over-privileged IT Director
$philId = (Get-MgUser -Filter "displayName eq 'Phil Greaves'").Id
@("SG-All-Staff", "SG-IT-Admins", "SG-Engineering", "SG-Finance-Readers",
  "SG-SOC-Operations", "SG-Project-Alpha", "Team-Office-Migration-2023") | ForEach-Object {
  $gid = (Get-MgGroup -Filter "displayName eq '$_'").Id
  New-MgGroupMember -GroupId $gid -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$philId"
  } -ErrorAction SilentlyContinue
}
Write-Host "Phil Greaves: 7 group memberships (cross-department accumulation)"

Create NE app registrations

These app registrations simulate the non-human identity landscape from IAM0.4 — a mix of active integrations, completed projects, and over-permissioned tools:

$appSpecs = @(
  @{ Name="NE-CRM-Integration"; Perms=@("User.Read.All") },
  @{ Name="NE-Data-Migration-Tool"; Perms=@("User.ReadWrite.All",
     "Group.ReadWrite.All", "Mail.ReadWrite", "Directory.ReadWrite.All") },
  @{ Name="NE-Backup-Service"; Perms=@("Mail.Read", "Files.Read.All") },
  @{ Name="NE-Dev-Test-App"; Perms=@("User.Read") },
  @{ Name="NE-Legacy-Reporting"; Perms=@("Files.ReadWrite.All",
     "Sites.ReadWrite.All") },
  @{ Name="NE-Vendor-SSO"; Perms=@("User.Read.All", "GroupMember.Read.All") }
)

$graphAppId = "00000003-0000-0000-c000-000000000000"

foreach ($spec in $appSpecs) {
  # Create app registration
  $app = New-MgApplication -DisplayName $spec.Name

  # Add a client secret
  $secret = Add-MgApplicationPassword -ApplicationId $app.Id -PasswordCredential @{
    DisplayName = "Lab secret"
    EndDateTime = (Get-Date).AddMonths(6)
  }

  Write-Host "Created app: $($spec.Name) — Secret expires: $(
    (Get-Date).AddMonths(6).ToString('yyyy-MM-dd'))"
}

# Create one app with an already-expired secret (for credential health testing)
$expiredApp = New-MgApplication -DisplayName "NE-Abandoned-POC"
# Note: You cannot create an already-expired secret via Graph API.
# The expired credential scenario will be simulated in Module 9.
Write-Host "Created app: NE-Abandoned-POC (credential expiry simulated in M9)"

Create guest accounts

Guest accounts simulate the stale external access pattern from IAM0.1:

Entra Admin Center — Manual Alternative

IdentityUsersAll usersNew userInvite external user
Enter the guest's email address and display name. Don't send the invitation email (toggle off Send invite message) — these are lab accounts that won't redeem the invitation. After creation, the guest appears in the user list with Invitation status: Pending. This pending state is deliberate — it reproduces the unredeemed invitation pattern from IAM0.1 where guest accounts exist in the directory with assigned access but the external user has never authenticated.

$guests = @(
  @{ Email="alex.rivera@contoso.com"; Name="Alex Rivera (Contoso)" },
  @{ Email="vendor.pentest@external.com"; Name="Vendor-PenTest" },
  @{ Email="sarah.kim@partner.com"; Name="Sarah Kim (Partner)" },
  @{ Email="old.contractor@legacy.com"; Name="Old Contractor" },
  @{ Email="project.contact@vendor.com"; Name="Project Contact (Vendor)" }
)

foreach ($g in $guests) {
  $invitation = New-MgInvitation -InvitedUserEmailAddress $g.Email `
    -InvitedUserDisplayName $g.Name `
    -InviteRedirectUrl "https://myapps.microsoft.com" `
    -SendInvitationMessage:$false

  Write-Host "Invited guest: $($g.Name) ($($g.Email))"
}

Write-Host "`n5 guest accounts created (invitations not sent — lab accounts)"

Assign admin roles to Phil (the over-privileged IT Director)

Phil holds multiple directory roles as permanent assignments — the separation of duties problem from IAM0.3. This is the most common privilege model in small-to-midsize organizations: one person has Global Administrator because they set up the tenant, and they accumulate additional roles as responsibilities grow. Nobody removes the earlier assignments because the admin center doesn't flag the accumulation as a problem.

Entra Admin Center — Manual Alternative

IdentityRoles & adminsRoles & admins → select Global AdministratorAdd assignments
Search for Phil Greaves and assign the role. Repeat for Exchange Administrator, SharePoint Administrator, and User Administrator. After all four assignments, select Phil's user account → Assigned roles. You should see all four roles listed with scope "/" (tenant-wide) and assignment type "Active" (permanent). This view is the separation of duties finding that Module 7 addresses — four tenant-wide permanent roles on a single identity, with no PIM activation, no approval workflow, and no time boundary.

$philId = (Get-MgUser -Filter "displayName eq 'Phil Greaves'").Id

$roles = @("Global Administrator", "Exchange Administrator",
  "SharePoint Administrator", "User Administrator")

foreach ($roleName in $roles) {
  $role = Get-MgDirectoryRole -Filter "displayName eq '$roleName'" -ErrorAction SilentlyContinue
  if (-not $role) {
    $template = Get-MgDirectoryRoleTemplate | Where-Object { $_.DisplayName -eq $roleName }
    $role = New-MgDirectoryRole -RoleTemplateId $template.Id
  }
  New-MgDirectoryRoleMember -DirectoryRoleId $role.Id -BodyParameter @{
    "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$philId"
  } -ErrorAction SilentlyContinue
  Write-Host "Assigned role: $roleName → Phil Greaves"
}

What the lab simulates and why

Each lab component exists to reproduce a specific governance gap that a module addresses. The lab isn't a simplified demo — it's a deliberately broken environment designed to generate real findings when you run the course diagnostics.

15 personas with mixed attribute coverage: 8 personas have complete governance attributes (department, hire date, manager). 7 have deliberate gaps — missing department, missing hire date, missing manager, or missing all three. When you run the data quality audit in IAM1.3, the coverage percentages reflect real incomplete data. When you deploy lifecycle workflows in Module 2, the workflows genuinely skip the personas with missing hire dates. The gap is structural, not simulated.

13 groups with mixed governance state: Some groups have owners and descriptions. Others are deliberately ownerless and undescribed. When you run the group architecture audit in IAM1.4, the ownerless and undescribed counts are real. When you deploy access reviews in Module 5, the reviews genuinely can't route to owners for the ownerless groups.

7 app registrations with credential patterns: Some have valid secrets, some have near-expiry secrets, one has no governance documentation. When you run the credential health audit in IAM0.4, the expired and near-expiry findings are genuine Graph API results, not mock data.

5 guest accounts (invitations not sent): All guests have "Pending" invitation status. Some have group memberships despite never authenticating. The stale guest pattern from IAM0.1 is live — the guests genuinely hold access in your tenant's directory without having verified their identity.

Phil's 4 permanent roles: The separation of duties finding from IAM0.3 is real — Phil genuinely holds Global Administrator, User Administrator, Exchange Administrator, and SharePoint Administrator as permanent tenant-wide assignments. Module 7's PIM conversion will convert these to eligible assignments with activation workflows.

The lab with 15 accounts produces the same governance patterns as a production tenant with 810. The percentages differ (15 accounts vs 810) but the structural gaps — missing attributes, ownerless groups, stale guests, over-privileged admins, ungoverned app registrations — are architecturally identical. The diagnostics you build work on both scales.

Cost modeling for production governance

The lab uses a free developer tenant and a Governance trial. In production, governance licensing has real cost. Understanding the cost model helps you build the business case in Module 13.

Without Entra ID Governance Licensing

The developer tenant and Governance trial cover the full course. In production, the Governance add-on costs $7/user/month on P1 or $4/user/month on P2. For NE's 810 users on E5: $3,240/month ($38,880/year). Without the Governance license, lifecycle workflows, entitlement management, and advanced access reviews are gated. The workaround: Graph API and PowerShell scripts that replicate approximately 70% of governance capability. The 30% gap — AI-assisted review recommendations, visual workflow designer, custom Logic App extensions — is documented as residual risk. IAM1.6 covers the full licensing landscape and workarounds.

Entra ID P2 — included in M365 E5 ($57/user/month). If you're on E3 ($36/user/month), P2 is an add-on at $9/user/month. P2 provides Identity Protection, risk-based Conditional Access, and basic PIM.

Entra ID Governance — add-on at $7/user/month on top of P2. Provides lifecycle workflows, entitlement management, access reviews with AI recommendations, and advanced PIM features. For an 810-person organization like NE, that's $5,670/month or $68,040/year.

Entra Suite — bundled license at $12/user/month that includes P2 + Governance + additional features (Verified ID, Internet Access, Private Access). For NE: $9,720/month or $116,640/year.

Agent 365 — $15/user/month for AI agent governance features (Entra Agent ID, agent blueprints, agent entitlement management). Pricing may change as the feature exits preview.

The "Without Governance Licensing" callouts throughout the course show what you can achieve with Graph API and PowerShell when the Governance license isn't available. The workarounds cover approximately 70% of governance capabilities. The 30% gap — AI-assisted review recommendations, advanced lifecycle workflow triggers, automated entitlement management — is documented as residual risk in each module's ADR.

At Northgate Engineering: Rachel Okafor's budget conversation with the CFO: Governance licensing for 810 users costs $68,040/year. A consulting firm's IAM implementation engagement costs $50,000–$150,000 and delivers a one-time configuration the internal team can't maintain. The course teaches your team to build and operate the program — ongoing capability, not a one-time project. The licensing cost is the operational expense. The capability is the asset.

Verify your lab environment

Run this verification script to confirm everything is set up correctly:

Write-Host "=== LAB ENVIRONMENT VERIFICATION ==="

$users = Get-MgUser -All -Property displayName, department, employeeHireDate
$nePersonas = $users | Where-Object {
  $_.DisplayName -match "Okafor|Ashworth|Sharma|Webb|Petrova|Greaves|Whitfield|Blackwood|Chen|Okonkwo|Taylor|Kowalski|Hughes|Al-Rashid|Morrison"
}
Write-Host "NE personas: $($nePersonas.Count) / 15 expected"

$groups = Get-MgGroup -All -Property displayName
$neGroups = $groups | Where-Object { $_.DisplayName -match "^(SG-|Team-)" }
Write-Host "NE groups: $($neGroups.Count) / 13 expected"

$apps = Get-MgApplication -All -Property displayName
$neApps = $apps | Where-Object { $_.DisplayName -like "NE-*" }
Write-Host "NE app registrations: $($neApps.Count) / 7 expected"

$guests = Get-MgUser -All | Where-Object { $_.UserType -eq "Guest" }
Write-Host "Guest accounts: $($guests.Count) / 5 expected"

# Check governance trial
try {
  $lcw = Get-MgIdentityGovernanceLifecycleWorkflow -ErrorAction Stop
  Write-Host "Governance trial: ACTIVE (lifecycle workflows accessible)"
} catch {
  Write-Host "Governance trial: NOT ACTIVE — activate before Module 2"
}

Write-Host "`n=== VERIFICATION COMPLETE ==="

If any count is wrong, re-run the relevant section above. The persona and group setup is idempotent — running it again won't create duplicates if the names already exist (the commands will fail silently on existing objects).

Tenant configuration baseline

Before starting Module 1, confirm your developer tenant has the default security configuration that the course assumes. These settings ship with the E5 developer license and shouldn't need changes, but verifying them prevents confusion when Module 6 (Application Access Governance) and Module 10 (Workload Identity) reference them.

Entra Admin Center — Configuration Baseline

IdentityApplicationsEnterprise applicationsConsent and permissionsUser consent settings
Verify the setting is "Allow user consent for apps from verified publishers, for selected permissions." This is the default for E5 tenants. Module 6 modifies the consent policy. Don't change it now.

IdentityUsersUser settings
Verify Users can register applications is set to "Yes" (default). Verify Restrict non-admin users from creating tenants is "Yes" (default). Module 6 tightens these settings as part of application access governance.

ProtectionConditional AccessPolicies
The developer tenant should have Security Defaults enabled (or a small set of default CA policies). Module 10 builds Conditional Access for workload identities. Don't create policies now — the course builds them with specific governance rationale documented in ADRs.

# Quick tenant configuration check
Write-Host "=== TENANT CONFIGURATION BASELINE ==="

# Check security defaults
$org = Get-MgOrganization
Write-Host "Tenant: $($org.DisplayName)"

# Check user settings
$authPolicy = Get-MgPolicyAuthorizationPolicy
Write-Host "Users can register apps: $($authPolicy.DefaultUserRolePermissions.AllowedToCreateApps)"
Write-Host "Users can create tenants: $(if ($authPolicy.DefaultUserRolePermissions.AllowedToCreateTenants) { 'Yes' } else { 'Restricted' })"

# Check CA policies
$caPolicies = Get-MgIdentityConditionalAccessPolicy -All -ErrorAction SilentlyContinue
Write-Host "Conditional Access policies: $(if ($caPolicies) { $caPolicies.Count } else { '0 (Security Defaults likely active)' })"

Write-Host "`nBaseline configuration verified. Do not modify these settings — the course changes them with documented ADR rationale."

Your lab environment is complete. 15 personas, 13 groups, 7 app registrations, 5 guest accounts, Phil's 4 over-privileged roles, the Governance trial, and a verified configuration baseline. Module 1 uses this environment for every diagnostic query.

Decision-point simulation

Scenario 1. Your E5 developer tenant's 90-day Entra ID Governance trial is about to expire. You're on Module 6 of 14. Do you renew the trial, purchase a Governance license, or restructure the remaining modules to avoid Governance features?

Renew the trial. Microsoft allows trial extensions for active developer tenants — the Developer Program dashboard shows renewal options. Purchasing a single Governance license ($7/user/month on P1) for your 15 test accounts costs $105/month, which is unnecessary for a learning environment. If renewal fails, the Budget Callout sections in each module document the Graph API and PowerShell workarounds that cover approximately 70% of Governance capability without the license.

Scenario 2. You want to test Conditional Access policies that block sign-ins from non-compliant devices. Your lab tenant has no Intune-managed devices. How do you approach this without enrolling a production device?

Use Report-Only mode. Conditional Access in Report-Only evaluates the policy against every sign-in without enforcing it. You see what would have been blocked in the sign-in logs without actually blocking anything. For device compliance testing specifically, create the CA policy targeting "Require compliant device," set it to Report-Only, and sign in from your lab machine. The sign-in log shows "Report-only: Failure" with the reason "Device is not compliant" — proving the policy logic works without needing an enrolled device. Module 5 covers this pattern in detail.

Next

IAM0.7 — Your IAM Program Package. The lab environment is ready. IAM0.7 introduces the five components of the IAM program package you'll build across the course — ADRs, governance cadence documents, risk register, compliance evidence, and executive summary. You'll set up the folder structure and understand what each module contributes to the final capstone deliverable.

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