In this module

MSA1.12 Module Lab — Identity Baseline

8 hours · Module 1 · Free

Module Lab — Building the Identity Baseline

This lab builds the identity structure in your M365 developer tenant. By the end, your tenant mirrors a realistic starting state — the good architecture (Cloud Sync with PHS, naming-convention groups, break-glass accounts) and the intentional gaps (MFA exclusion, stale guests, ownerless groups, accumulated access from role changes). The gaps are what you diagnose and fix across MSA2MSA14. A clean tenant teaches you to maintain an ideal state. A realistically messy tenant teaches you to improve a real one — which is the skill you'll actually use at work.

Prerequisites: M365 E5 developer tenant configured in MSA0.6. Microsoft Graph PowerShell module installed (Install-Module Microsoft.Graph). Global Administrator access. 30 minutes minimum.

Estimated time: 60–90 minutes (including verification).

Step 1 — Create test persona accounts with architectural context

These 10 accounts represent the persona groups from MSA0.5. Each has specific attributes that drive downstream architecture: Department determines dynamic group membership (MSA1.10), JobTitle determines role-based access, and UsageLocation is required for license assignment. The attributes aren't cosmetic — they're the data that lifecycle workflows, dynamic groups, and CA policies consume.

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

$domain = (Get-MgOrganization).VerifiedDomains |
  Where-Object { $_.IsDefault } |
  Select-Object -ExpandProperty Name

$password = @{ Password = "NE-Lab-2026!"; ForceChangePasswordNextSignIn = $false }

# Each persona maps to a specific architectural role in the course
$personas = @(
  # Security team — E5 users, SOC operations, architecture decisions
  @{ First="Rachel"; Last="Okafor"; UPN="r.okafor"; Dept="Security";
     Title="CISO"; Site="Manchester"; Licence="E5";
     Role="CISO — presents to board, approves ADRs, owns risk register" }
  @{ First="Marcus"; Last="Webb"; UPN="m.webb"; Dept="Security";
     Title="Security Architect"; Site="Manchester"; Licence="E5";
     Role="Identity architect — designs controls, writes ADRs, manages AU-scoped roles" }
  @{ First="Tom"; Last="Ashworth"; UPN="t.ashworth"; Dept="Security";
     Title="SOC Analyst L1"; Site="Manchester"; Licence="E5";
     Role="SOC analyst — monitors sign-in logs, investigates alerts" }
  @{ First="Priya"; Last="Sharma"; UPN="p.sharma"; Dept="Security";
     Title="SOC Analyst L1"; Site="Manchester"; Licence="E5";
     Role="SOC analyst — THE mover problem example (was Engineering, now SOC)" }
  # IT team — E5 users, infrastructure management
  @{ First="Phil"; Last="Greaves"; UPN="p.greaves"; Dept="IT";
     Title="IT Director"; Site="Manchester"; Licence="E5";
     Role="IT Director — THE MFA exclusion example (excluded from MFA by mistake)" }
  @{ First="Elena"; Last="Petrova"; UPN="e.petrova"; Dept="IT";
     Title="GRC Analyst"; Site="Manchester"; Licence="E5";
     Role="GRC analyst — compliance evidence, audit documentation" }
  # Business users — E3 users, different department/site combinations
  @{ First="Emma"; Last="Richardson"; UPN="e.richardson"; Dept="Engineering";
     Title="Senior Engineer"; Site="Newcastle"; Licence="E3";
     Role="Engineering user at remote site — tests site-based CA policies" }
  @{ First="James"; Last="Carroll"; UPN="j.carroll"; Dept="Finance";
     Title="Finance Manager"; Site="Manchester"; Licence="E5";
     Role="Finance user — tests sensitive-department CA policies" }
  @{ First="Sarah"; Last="Mitchell"; UPN="s.mitchell"; Dept="HR";
     Title="HR Manager"; Site="Manchester"; Licence="E3";
     Role="HR manager — lifecycle workflow testing, HR data pipeline" }
  @{ First="David"; Last="Chen"; UPN="d.chen"; Dept="Operations";
     Title="Operations Manager"; Site="Leeds"; Licence="E3";
     Role="Remote site user — tests location-based CA policies" }
)

Write-Host "Creating test persona accounts..."
foreach ($p in $personas) {
  $user = New-MgUser -DisplayName "$($p.First) $($p.Last)" `
    -UserPrincipalName "$($p.UPN)@$domain" `
    -MailNickname $p.UPN `
    -PasswordProfile $password `
    -AccountEnabled `
    -Department $p.Dept `
    -JobTitle $p.Title `
    -City $p.Site `
    -UsageLocation "GB"
  Write-Host "  ✓ $($user.DisplayName) — $($p.Dept), $($p.Title), $($p.Site) [$($p.Licence)]"
}
Write-Host ""
Write-Host "Created $($personas.Count) persona accounts"

The City attribute stores the site location. In production, organizations use the on-premises OU path (OnPremisesDistinguishedName) to identify site membership, but in a cloud-only developer tenant, the City attribute serves the same purpose for dynamic group rules and AU population.

Entra Admin Center

Verify accounts:
IdentityUsersAll users
Confirm 10 new accounts. Click each one → Properties to verify Department, Job title, and City are populated. These attributes drive dynamic groups and lifecycle workflows — if they're missing, downstream automation fails.

Step 2 — Create break-glass accounts with architectural rationale

Break-glass accounts are emergency access identities that bypass normal controls by design. They're cloud-only (no on-premises dependency — they work even if AD or Cloud Sync fails), they're excluded from CA policies (they can authenticate when CA locks everyone out), and they must be monitored (any sign-in triggers an immediate alert because they should almost never be used).

$bgPassword = @{ Password = "BG-Emergency-NE-2026!!"; ForceChangePasswordNextSignIn = $false }

$bg01 = New-MgUser -DisplayName "Break Glass 01" `
  -UserPrincipalName "bg01@$domain" `
  -MailNickname "bg01" `
  -PasswordProfile $bgPassword `
  -AccountEnabled `
  -UsageLocation "GB"

$bg02 = New-MgUser -DisplayName "Break Glass 02" `
  -UserPrincipalName "bg02@$domain" `
  -MailNickname "bg02" `
  -PasswordProfile $bgPassword `
  -AccountEnabled `
  -UsageLocation "GB"

Write-Host "Break-glass accounts created:"
Write-Host "  ✓ bg01@$domain — Cloud-only, no MFA (by design), excluded from CA"
Write-Host "  ✓ bg02@$domain — Cloud-only, no MFA (by design), excluded from CA"
Write-Host ""
Write-Host "IMPORTANT: In production, these accounts would have:"
Write-Host "  - Passwords stored in a physical safe (two halves, two locations)"
Write-Host "  - Global Administrator role assigned"
Write-Host "  - Sentinel alert rule for any sign-in event"
Write-Host "  - Membership in restricted management AU (MSA1.6)"
Write-Host "  - NO MFA registered (they bypass MFA by design)"

Step 3 — Create the Administrative Unit structure from MSA1.6

The AU design from MSA1.6 creates site-based delegation boundaries and a restricted AU for break-glass protection. In the developer tenant, we create the Manchester AU and the restricted AU to demonstrate the architecture.

# Site AU — Manchester (the primary site with most personas)
$manchesterAU = New-MgDirectoryAdministrativeUnit -BodyParameter @{
  displayName  = "Site-Manchester"
  description  = "Manchester head office — site-level admin delegation"
  membershipType = "Assigned"
}
Write-Host "✓ Created AU: Site-Manchester"

# Restricted AU — break-glass protection
$restrictedAU = New-MgDirectoryAdministrativeUnit -BodyParameter @{
  displayName = "Restricted-Privileged"
  description = "Restricted management AU — break-glass and privileged accounts"
  membershipType = "Assigned"
  isMemberManagementRestricted = $true
}
Write-Host "✓ Created AU: Restricted-Privileged (RESTRICTED)"

# Add Manchester personas to the Manchester AU
$manchesterPersonas = @("r.okafor","m.webb","t.ashworth","p.sharma",
                        "p.greaves","e.petrova","j.carroll","s.mitchell")
foreach ($upn in $manchesterPersonas) {
  $user = Get-MgUser -UserId "$upn@$domain"
  New-MgDirectoryAdministrativeUnitMemberByRef `
    -AdministrativeUnitId $manchesterAU.Id `
    -BodyParameter @{
      "@odata.id" = "https://graph.microsoft.com/v1.0/users/$($user.Id)"
    }
}
Write-Host "  Added $($manchesterPersonas.Count) personas to Site-Manchester"

# Add break-glass accounts to restricted AU
foreach ($bgUPN in @("bg01","bg02")) {
  $bgUser = Get-MgUser -UserId "$bgUPN@$domain"
  New-MgDirectoryAdministrativeUnitMemberByRef `
    -AdministrativeUnitId $restrictedAU.Id `
    -BodyParameter @{
      "@odata.id" = "https://graph.microsoft.com/v1.0/users/$($bgUser.Id)"
    }
}
Write-Host "  Added bg01, bg02 to Restricted-Privileged"

# Assign the security architect as AU-scoped User Administrator for restricted AU
$userAdminTemplate = Get-MgDirectoryRoleTemplate |
  Where-Object { $_.DisplayName -eq "User Administrator" }
$marcus = Get-MgUser -UserId "m.webb@$domain"
New-MgRoleManagementDirectoryRoleAssignment -BodyParameter @{
  roleDefinitionId = $userAdminTemplate.Id
  principalId      = $marcus.Id
  directoryScopeId = "/administrativeUnits/$($restrictedAU.Id)"
}
Write-Host "  ✓ the security architect assigned User Administrator scoped to Restricted-Privileged"

Entra Admin Center

Verify AUs:
IdentityRoles & adminsAdmin units
Confirm 2 AUs. Click Restricted-Privileged — verify the shield icon indicating restricted management. Click Members to confirm bg01 and bg02 are listed.

Test the restriction:
Try to reset bg01's password from IdentityUsers → select bg01 → Reset password. If signed in as Global Admin without AU-scoped role, you should see an error message about restricted management.

Step 4 — Create naming-convention groups with type rationale

Each group follows the MSA1.9 naming convention. The type selection (security vs M365 vs dynamic) follows the MSA1.10 design principles: security groups for access control, M365 groups for collaboration, dynamic groups for attribute-driven populations.

# Department security groups (access control — used by CA policies and app assignments)
$depts = @("Security","IT","Engineering","Finance","HR","Operations","Sales","Marketing","Legal","Facilities")
foreach ($dept in $depts) {
  New-MgGroup -DisplayName "SG-$dept-All" -MailEnabled:$false `
    -MailNickname "sg-$($dept.ToLower())-all" -SecurityEnabled `
    -Description "$dept department — all staff. Used for CA targeting and application access."
  Write-Host "  ✓ SG-$dept-All (Security, Assigned)"
}

# Site security groups (CA policy targeting for location-based policies)
$sites = @("Manchester","Newcastle","Leeds")
foreach ($site in $sites) {
  New-MgGroup -DisplayName "SG-$site-Staff" -MailEnabled:$false `
    -MailNickname "sg-$($site.ToLower())-staff" -SecurityEnabled `
    -Description "$site site — all staff. Used for site-based CA policies."
  Write-Host "  ✓ SG-$site-Staff (Security, Assigned)"
}

# Licence groups (group-based licensing — MSA1.10)
New-MgGroup -DisplayName "SG-Licence-E5" -MailEnabled:$false `
  -MailNickname "sg-license-e5" -SecurityEnabled `
  -Description "E5 license assignment group. Members receive Microsoft 365 E5."
New-MgGroup -DisplayName "SG-Licence-E3" -MailEnabled:$false `
  -MailNickname "sg-license-e3" -SecurityEnabled `
  -Description "E3 license assignment group. Members receive Microsoft 365 E3."
Write-Host "  ✓ SG-Licence-E5, SG-Licence-E3 (Licence assignment)"

# CA exclusion groups
New-MgGroup -DisplayName "SG-CA-Exclude-BreakGlass" -MailEnabled:$false `
  -MailNickname "sg-ca-exclude-breakglass" -SecurityEnabled `
  -Description "Break-glass accounts excluded from all CA policies."
Write-Host "  ✓ SG-CA-Exclude-BreakGlass (CA exclusion)"

# Application access groups (used by MSA4 for app assignments)
$appGroups = @(
  @{Name="SG-App-Sentinel-Readers"; Desc="Sentinel workspace reader access"}
  @{Name="SG-App-Sentinel-Contributors"; Desc="Sentinel workspace contributor access"}
  @{Name="SG-App-CAD-Users"; Desc="CAD application access (Engineering)"}
  @{Name="SG-App-PLM-Users"; Desc="PLM application access (Engineering)"}
)
foreach ($ag in $appGroups) {
  New-MgGroup -DisplayName $ag.Name -MailEnabled:$false `
    -MailNickname ($ag.Name.ToLower() -replace '[^a-z0-9-]','') -SecurityEnabled `
    -Description $ag.Desc
  Write-Host "  ✓ $($ag.Name)"
}

# M365 collaboration group (project team — creates Teams channel + SharePoint)
New-MgGroup -DisplayName "M365-Project-Safety-Compliance" -MailEnabled `
  -MailNickname "m365-project-safety" -SecurityEnabled `
  -GroupTypes @("Unified") `
  -Description "Safety Compliance project collaboration — Teams + SharePoint"
Write-Host "  ✓ M365-Project-Safety-Compliance (M365/Teams)"

$totalGroups = (Get-MgGroup -All).Count
Write-Host ""
Write-Host "Total groups in tenant: $totalGroups"

Step 5 — Populate groups and assign licenses

function Add-ToGroup($userUPN, $groupName) {
  $user = Get-MgUser -UserId "$userUPN@$domain"
  $group = Get-MgGroup -Filter "displayName eq '$groupName'"
  try {
    New-MgGroupMember -GroupId $group.Id -DirectoryObjectId $user.Id
  } catch {
    # Ignore "already a member" errors
    if ($_.Exception.Message -notmatch "already exist") { throw }
  }
}

Write-Host "Populating groups..."

# Department memberships (based on each persona's Department attribute)
Add-ToGroup "r.okafor" "SG-Security-All"
Add-ToGroup "m.webb" "SG-Security-All"
Add-ToGroup "t.ashworth" "SG-Security-All"
Add-ToGroup "p.sharma" "SG-Security-All"
Add-ToGroup "p.greaves" "SG-IT-All"
Add-ToGroup "e.petrova" "SG-IT-All"
Add-ToGroup "e.richardson" "SG-Engineering-All"
Add-ToGroup "j.carroll" "SG-Finance-All"
Add-ToGroup "s.mitchell" "SG-HR-All"
Add-ToGroup "d.chen" "SG-Operations-All"

# Site memberships
$manchesterUsers = @("r.okafor","m.webb","t.ashworth","p.sharma","p.greaves","e.petrova","j.carroll","s.mitchell")
foreach ($u in $manchesterUsers) { Add-ToGroup $u "SG-Manchester-Staff" }
Add-ToGroup "e.richardson" "SG-Newcastle-Staff"
Add-ToGroup "d.chen" "SG-Leeds-Staff"

# Break-glass to CA exclusion
Add-ToGroup "bg01" "SG-CA-Exclude-BreakGlass"
Add-ToGroup "bg02" "SG-CA-Exclude-BreakGlass"

# Application access
Add-ToGroup "t.ashworth" "SG-App-Sentinel-Readers"
Add-ToGroup "p.sharma" "SG-App-Sentinel-Readers"
Add-ToGroup "m.webb" "SG-App-Sentinel-Contributors"
Add-ToGroup "e.richardson" "SG-App-CAD-Users"
Add-ToGroup "e.richardson" "SG-App-PLM-Users"

# Licence groups — E5 users
foreach ($u in @("r.okafor","m.webb","t.ashworth","p.sharma","p.greaves","e.petrova","j.carroll")) {
  Add-ToGroup $u "SG-Licence-E5"
}
# E3 users
foreach ($u in @("e.richardson","s.mitchell","d.chen")) {
  Add-ToGroup $u "SG-Licence-E3"
}

Write-Host ""
Write-Host "Group population complete"

Entra Admin Center

Assign licenses via groups:
IdentityGroups → select SG-Licence-E5LicensesAssignments+ Assignments → select Microsoft 365 E5 DeveloperSave
Repeat for SG-Licence-E3 with the E3 license (if available in your developer tenant).

Check Processing status after assignment — confirm all members received their license successfully. Common errors: UsageLocation not set (we set GB for all personas) or insufficient available licenses.

Step 6 — Create intentional gaps (the organization's current-state problems)

These gaps replicate the specific problems documented across MSA1.1–1.11. Each gap maps to a future module that fixes it. The learner diagnoses and remediates these across the course.

Write-Host "Creating intentional gaps..."

# Gap 1 — the IT Director excluded from MFA
# (MSA0.9 Scenario 6, MSA2 fixes this by removing the exclusion)
New-MgGroup -DisplayName "SG-CA-Exclude-MFA" -MailEnabled:$false `
  -MailNickname "sg-ca-exclude-mfa" -SecurityEnabled `
  -Description "MFA exclusion — THIS GROUP SHOULD NOT EXIST IN PRODUCTION"
$mfaExclude = Get-MgGroup -Filter "displayName eq 'SG-CA-Exclude-MFA'"
$phil = Get-MgUser -UserId "p.greaves@$domain"
New-MgGroupMember -GroupId $mfaExclude.Id -DirectoryObjectId $phil.Id
Write-Host "  ⚠ Gap 1: the IT Director in MFA exclusion group (MSA2 removes this)"

# Gap 2 — Stale guest account with pending invitation
# (MSA1.8 teaches you to find pending invitations — oldest 3+ years)
$invitation = New-MgInvitation `
  -InvitedUserEmailAddress "stale.contractor@external-partner.com" `
  -InviteRedirectUrl "https://myapps.microsoft.com" `
  -SendInvitationMessage:$false
Write-Host "  ⚠ Gap 2: Stale guest invitation created (MSA1.8/MSA12 cleans this up)"

# Gap 3 — Empty group with bad naming (group sprawl indicator)
# (MSA1.10 teaches you to find empty groups)
New-MgGroup -DisplayName "old contractors group" -MailEnabled:$false `
  -MailNickname "old-contractors" -SecurityEnabled
Write-Host "  ⚠ Gap 3: Empty group 'old contractors group' — no naming convention, no members"

# Gap 4 — Ownerless group with no description
# (MSA1.10 teaches you to find ownerless groups)
New-MgGroup -DisplayName "Project Safety" -MailEnabled:$false `
  -MailNickname "project-safety" -SecurityEnabled
Write-Host "  ⚠ Gap 4: 'Project Safety' — no owner, no description, bad naming"

# Gap 5 — accumulated access from role changes (the mover problem)
# (MSA1.7 documented 11 groups / 8 stale — Engineering + Safety project)
Add-ToGroup "p.sharma" "SG-Engineering-All"
Add-ToGroup "p.sharma" "SG-App-CAD-Users"
Add-ToGroup "p.sharma" "SG-App-PLM-Users"
Add-ToGroup "p.sharma" "M365-Project-Safety-Compliance"
Write-Host "  ⚠ Gap 5: Priya has Engineering + SOC access (mover accumulation — MSA1.7/MSA12)"

# Gap 6 — No naming policy configured
# (MSA1.9 found 0% CA policy naming compliance and no blocked words)
Write-Host "  ⚠ Gap 6: No group naming policy configured (MSA1.9 designs this)"

# Gap 7 — Guest permissions at default (not restricted)
# (MSA1.3 found guestUserRoleId at default — guests can enumerate directory)
Write-Host "  ⚠ Gap 7: Guest permissions at default level — directory enumeration allowed (MSA3 restricts)"

Write-Host ""
Write-Host "All gaps created. These mirror the organization's assessed state from MSA1.1-1.11."

Step 7 — Configure the group naming policy

MSA1.9 designed the naming convention but the lab needs the enforcement configured. The naming policy blocks common bad-practice words and appends the creator's department to M365 group names.

Connect-MgGraph -Scopes "Directory.ReadWrite.All"

# Check if the Group.Unified settings template exists
$template = Get-MgBetaDirectorySettingTemplate |
  Where-Object { $_.DisplayName -eq "Group.Unified" }

if ($template) {
  $existingSettings = Get-MgBetaDirectorySetting |
    Where-Object { $_.TemplateId -eq $template.Id }

  if (-not $existingSettings) {
    # Create new naming policy
    $params = @{
      templateId = $template.Id
      values = @(
        @{ name = "PrefixSuffixNamingRequirement"; value = "[GroupName]-[Department]" }
        @{ name = "CustomBlockedWordsList"; value = "test,temp,old,delete,copy,backup,dummy,sample,fake,draft" }
      )
    }
    New-MgBetaDirectorySetting -BodyParameter $params
    Write-Host "✓ Group naming policy created"
    Write-Host "  Suffix: -[Department] (auto-appended from creator's attribute)"
    Write-Host "  Blocked words: test, temp, old, delete, copy, backup, dummy, sample, fake, draft"
  } else {
    Write-Host "Naming policy already exists — updating..."
    $params = @{
      values = @(
        @{ name = "PrefixSuffixNamingRequirement"; value = "[GroupName]-[Department]" }
        @{ name = "CustomBlockedWordsList"; value = "test,temp,old,delete,copy,backup,dummy,sample,fake,draft" }
      )
    }
    Update-MgBetaDirectorySetting -DirectorySettingId $existingSettings.Id -BodyParameter $params
    Write-Host "✓ Group naming policy updated"
  }
} else {
  Write-Host "Group.Unified template not available in this tenant"
}

Entra Admin Center

Verify naming policy:
IdentityGroupsAll groupsNaming policy
The Group name policy tab should show the suffix rule. The Blocked words tab should show 10 blocked terms.

Test the policy:
Try creating a new M365 group from the admin center with the name "Test Project." The blocked word "test" should be rejected. Try creating one named "Alpha Project" — it should succeed and automatically append the department suffix (e.g., "Alpha Project-IT" if created by a user in the IT department).

Step 8 — Test AU restriction protection

This step verifies that the restricted management AU actually prevents tenant-scoped admins from modifying break-glass accounts. This is the architecture from MSA1.6 — and the test confirms it works in your lab.

# Verify the restricted AU is protecting break-glass accounts
# This query should work (Marcus has AU-scoped User Admin role)
Write-Host "Testing AU restriction..."
Write-Host ""

$bg01 = Get-MgUser -UserId "bg01@$domain"

# Test 1: Try to get the user (should work — read access is fine)
Write-Host "Test 1 — Read access to bg01: SUCCESS (read is not restricted)"
Write-Host "  DisplayName: $($bg01.DisplayName)"
Write-Host ""

# Test 2: Document what would happen with a password reset
Write-Host "Test 2 — Password reset attempt:"
Write-Host "  A Global Admin WITHOUT AU-scoped role would see an error:"
Write-Host "  'Insufficient privileges to complete the operation'"
Write-Host "  A User Admin WITH AU-scoped role (Marcus) can reset the password."
Write-Host ""

Write-Host "To test this manually:"
Write-Host "  1. Sign in to Entra admin center as your Global Admin"
Write-Host "  2. Navigate to Identity → Users → Break Glass 01"
Write-Host "  3. Click 'Reset password'"
Write-Host "  4. You should see a permissions error if the restricted AU is working"
Write-Host ""
Write-Host "This confirms the break-glass accounts are protected from"
Write-Host "tenant-scoped admin compromise — only Marcus (with AU-scoped"
Write-Host "User Administrator) can modify them."

Entra Admin Center

Manual AU restriction test:
IdentityUsers → select Break Glass 01Reset password
If signed in as Global Administrator (without AU-scoped role on the restricted AU), you should receive a permissions error. This confirms the architectural protection is active.

Step 9 — Comprehensive verification

Run the full census against your lab tenant to confirm it matches the expected state:

Write-Host "============================================="
Write-Host "  IDENTITY BASELINE — LAB VERIFICATION"
Write-Host "============================================="
Write-Host ""

# User census
$allUsers = Get-MgUser -All -Property UserType, AccountEnabled, Department
$members = $allUsers | Where-Object { $_.UserType -eq 'Member' }
$guests = $allUsers | Where-Object { $_.UserType -eq 'Guest' }
Write-Host "USERS"
Write-Host "  Member accounts:     $($members.Count) (expected: 12 — 10 personas + 2 break-glass)"
Write-Host "  Guest accounts:      $($guests.Count) (expected: 1 — stale invitation)"
Write-Host ""

# Group census
$allGroups = Get-MgGroup -All -Property DisplayName, SecurityEnabled, GroupTypes
$sgGroups = $allGroups | Where-Object { $_.DisplayName -match '^SG-' }
$m365Groups = $allGroups | Where-Object { "Unified" -in $_.GroupTypes }
$badGroups = $allGroups | Where-Object { $_.DisplayName -notmatch '^(SG-|M365-|DG-|DL-)' }
Write-Host "GROUPS"
Write-Host "  Total groups:        $($allGroups.Count)"
Write-Host "  SG- prefix:          $($sgGroups.Count)"
Write-Host "  M365 groups:         $($m365Groups.Count)"
Write-Host "  Non-compliant names: $($badGroups.Count) (expected: 2 — 'old contractors', 'Project Safety')"
Write-Host ""

# AU census
$aus = Get-MgDirectoryAdministrativeUnit -All
Write-Host "ADMINISTRATIVE UNITS"
Write-Host "  Total AUs:           $($aus.Count) (expected: 2 — Manchester + Restricted)"
$aus | ForEach-Object {
  $restricted = if ($_.IsMemberManagementRestricted) { " [RESTRICTED]" } else { "" }
  Write-Host "    $($_.DisplayName)$restricted"
}
Write-Host ""

# Priya's mover accumulation check
$priyaGroups = Get-MgUserMemberOf -UserId "p.sharma@$domain" -All |
  Where-Object { $_.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.group' }
Write-Host "MOVER PROBLEM CHECK"
Write-Host "  Total group memberships: $($priyaGroups.Count)"
$priyaGroups | ForEach-Object {
  $name = $_.AdditionalProperties['displayName']
  $stale = if ($name -match 'Engineering|CAD|PLM|Safety') { " ⚠ STALE" } else { "" }
  Write-Host "    $name$stale"
}
Write-Host ""

# Gap summary
Write-Host "INTENTIONAL GAPS (to be fixed across MSA2-MSA14)"
Write-Host "  1. the IT Director in MFA exclusion         → MSA2 removes"
Write-Host "  2. Stale guest (PendingAcceptance)        → MSA1.8/MSA12 cleans"
Write-Host "  3. Empty group 'old contractors group'    → MSA1.10 deletes"
Write-Host "  4. Ownerless group 'Project Safety'       → MSA1.10 assigns owner or deletes"
Write-Host "  5. Priya: Engineering + SOC accumulation  → MSA1.7/MSA12 mover workflow"
Write-Host "  6. No group naming policy                 → MSA1.9 configures"
Write-Host "  7. Guest permissions at default           → MSA3 restricts"
Write-Host ""
Write-Host "============================================="
Write-Host "  LAB BASELINE COMPLETE"
Write-Host "============================================="

Entra Admin Center

Visual verification checklist:
IdentityUsers — 12 members + 1 guest = 13 total
IdentityGroups — ~23 groups, most with SG- prefix, 2 non-compliant
IdentityRoles & adminsAdmin units — 2 AUs, one with shield icon
IdentityUsers → SOC Analyst 2 → Groups — should show both Security and Engineering groups

Lab complete — what you've built

Your developer tenant now contains:

  • 12 member accounts (10 personas + 2 break-glass) with Department, JobTitle, City, and UsageLocation set
  • 2 Administrative Units (1 site AU with 8 Manchester personas, 1 restricted AU with break-glass accounts and the security architect as AU-scoped admin)
  • ~23 groups following the naming convention (department, site, license, CA exclusion, application, project)
  • 7 intentional gaps that mirror the organization's assessed state — each mapped to the module that remediates it
  • 1 stale guest invitation representing the organization's 98 stale guests
  • SOC Analyst 2's accumulated access representing the mover problem across the entire workforce

Every subsequent module builds on this baseline. MSA2 designs authentication (and removes the MFA exclusion). MSA3 designs CA policies (targeting the groups you created, excluding the break-glass group). MSA4 designs PIM (using role-assignable groups you'll create from the SG-Role- pattern). MSA12 designs governance (access reviews that catch Priya's stale access, guest reviews that find the stale invitation, group lifecycle that deletes the empty groups).

Next
**MSA1.13 — Guided Walkthrough.** A narrative that connects every MSA1 finding into a single coherent story about the organization's identity architecture.

You're reading the free modules of m365-security-architecture

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

View Pricing See Full Syllabus