In this module
MSA1.9 Naming Conventions and Governance Foundations
Naming conventions feel like administrative overhead — the kind of standard that goes in a governance document nobody reads. But if you've ever tried to audit Conditional Access policies in a tenant where they're named "Test Policy," "MFA Policy," "New Policy 2," and "Copy of MFA Policy," you understand the problem at a visceral level. You can't query what you can't parse. You can't automate what you can't pattern-match. At the organization's scale (214 groups, 34 app registrations, 6 CA policies, and the AU structure from MSA1.6), inconsistent naming turns every governance operation into manual interpretation. This sub teaches naming as the architectural foundation that determines whether your identity governance is programmable or manual.
Most tenants have groups with no naming convention. CA policies are named after what someone was thinking when they created them. Application registrations use developer-chosen names that reveal nothing about ownership, environment, or purpose. Without a naming convention, every governance query requires manual inspection. With one, you can programmatically answer "how many groups exist for Engineering?" or "which CA policies target guest users?" in a single Graph API call. This sub designs the naming taxonomy for every object type in Entra ID and establishes the enforcement mechanism that prevents the convention from degrading over time.
Estimated time: 45 minutes.
Why naming is architecture — a detailed worked example
Consider this governance scenario. the security architect (the organization's Security Architect) needs to answer the CISO's (CISO) question: "We're onboarding 40 new engineers next month. What groups will they need to join, and are those groups configured correctly?"
With consistent naming, Marcus runs one query:
Get-MgGroup -All -Property DisplayName, GroupTypes, MembershipRule, MembershipRuleProcessingState |
Where-Object { $_.DisplayName -match '^SG-Engineering|^DG-Engineering|^M365-Engineering' } |
Select-Object DisplayName,
@{N='Type';E={if($_.GroupTypes -contains 'Unified'){'M365'}
elseif($_.MembershipRule){'Dynamic'}
else{'Security'}}},
@{N='Membership';E={if($_.MembershipRule){'Dynamic: '+$_.MembershipRule}else{'Assigned'}}} |
Format-Table -AutoSizeThe query returns every Engineering group, identified by the naming convention, with their type and membership method. Marcus can immediately see which groups the new engineers need, whether the groups are dynamic (membership updates automatically when Department=Engineering) or assigned (someone must manually add the 40 new hires), and whether any are misconfigured.
Without consistent naming, Marcus has to search the directory for groups that might be related to Engineering. "Engineering - All Staff" — is that the department group? "engineering team" — is that the same group or a different one? "Tom's test group" — Tom is in Engineering, but is this an Engineering group? "Project Safety" — some engineers are in this project, but it's not an Engineering-specific group. Marcus can't distinguish Engineering groups from other groups by name. He has to open each one, inspect the members, check the description (if it has one), and make a judgment call. For 214 groups, this takes hours.
The naming convention turns a multi-hour investigation into a 10-second query. That's not administrative convenience — it's the difference between governance that's practical and governance that's theoretical.
the organization's current naming state — the chaos inventory
Connect-MgGraph -Scopes "Group.Read.All","Policy.Read.All","Application.Read.All"
$groups = Get-MgGroup -All -Property DisplayName, GroupTypes, SecurityEnabled, MailEnabled
# Detect groups with structured prefixes
$prefixPattern = '^(SG-|DG-|DL-|M365-|Team-|SP-|App-|CA-|Role-|Licence-)'
$prefixed = ($groups | Where-Object { $_.DisplayName -match $prefixPattern }).Count
$unprefixed = ($groups | Where-Object { $_.DisplayName -notmatch $prefixPattern }).Count
Write-Host "=== Group Naming Compliance ==="
Write-Host "Total groups: $($groups.Count)"
Write-Host "Follow prefix pattern: $prefixed ($([math]::Round($prefixed/$groups.Count*100))%)"
Write-Host "No prefix pattern: $unprefixed ($([math]::Round($unprefixed/$groups.Count*100))%)"
Write-Host ""
# Show naming chaos examples
Write-Host "Sample non-compliant group names:"
$groups | Where-Object { $_.DisplayName -notmatch $prefixPattern } |
Select-Object -First 15 DisplayName |
ForEach-Object { Write-Host " ❌ $($_.DisplayName)" }=== Group Naming Compliance ===
Total groups: 214
Follow prefix pattern: 87 (41%)
No prefix pattern: 127 (59%)
Sample non-compliant group names:
❌ finance team
❌ Engineering - All Staff
❌ Tom's test group
❌ Backup Admins 2
❌ Manchester Office
❌ Project Safety
❌ all company
❌ IT Security Team
❌ SOC Analysts
❌ Marketing email list
❌ old contractors group
❌ Executive team
❌ Leeds site users
❌ Birmingham Staff
❌ HR Department59% of groups have no structured naming. The problems are visible: inconsistent casing ("finance team" vs "IT Security Team"), inconsistent separators (spaces, hyphens, nothing), no type indicator (you can't tell a security group from a distribution list from a Teams-connected M365 group by name), no scope indicator (is "Manchester Office" a site group, a building group, a SharePoint group?), and obvious disposable groups that were never cleaned up ("Tom's test group," "old contractors group," "Backup Admins 2").
Now assess CA policy and app registration naming:
# CA policy naming
$caPolicies = Get-MgIdentityConditionalAccessPolicy -All
Write-Host "=== CA Policy Naming ==="
Write-Host "Total policies: $($caPolicies.Count)"
$caPolicies | ForEach-Object {
$compliant = if ($_.DisplayName -match '^CA-') { "✓" } else { "❌" }
Write-Host " $compliant $($_.DisplayName) [$($_.State)]"
}
Write-Host ""
# App registration naming
$apps = Get-MgApplication -All -Property DisplayName
$appCompliant = ($apps | Where-Object { $_.DisplayName -match '^App-' }).Count
Write-Host "=== App Registration Naming ==="
Write-Host "Total registrations: $($apps.Count)"
Write-Host "Follow 'App-' prefix: $appCompliant ($([math]::Round($appCompliant/$apps.Count*100))%)"
Write-Host ""
$apps | Where-Object { $_.DisplayName -notmatch '^App-' } |
Select-Object -First 8 DisplayName |
ForEach-Object { Write-Host " ❌ $($_.DisplayName)" }=== CA Policy Naming ===
Total policies: 6
❌ Block legacy auth [enabled]
❌ Require MFA for admins [enabled]
❌ MFA for all users [enabledForReportingButNotEnforced]
❌ Test - guest MFA [disabled]
❌ Block risky sign-ins [enabled]
❌ New policy [disabled]
=== App Registration Naming ===
Total registrations: 34
Follow 'App-' prefix: 0 (0%)
❌ CRM Integration
❌ ERP Connector
❌ SharePoint Automation
❌ Backup Service
❌ HR Data Sync
❌ Reporting Dashboard
❌ IT Helpdesk Bot
❌ Visitor ManagementZero CA policies follow a structured convention. Zero app registrations follow a structured convention. The naming is entirely ad-hoc across all object types.
The naming taxonomy — design and rationale
A naming convention has three properties: consistent (every object of the same type uses the same format), parseable (you can extract meaning from the name programmatically — type, scope, purpose — without inspecting the object), and enforceable (violations can be detected automatically).
Groups: {Type}-{Scope}-{Purpose}
The group naming convention encodes three pieces of information:
Component Values What it tells you
--------- --------------------------------- ------------------------------------------
{Type} SG- Security group (access) What kind of group (determines what it
M365- Microsoft 365 (collaboration) can be used for, what it can contain,
DL- Distribution list (email) and how it's managed)
DG- Dynamic group (rule-based)
{Scope} All-Staff Who it's for (department, site, project,
{Department} e.g., Engineering application, license tier, CA policy
{Site} e.g., Manchester exclusion, or Entra role)
{Project} e.g., Safety-Compliance
{Application} e.g., Sentinel
Licence-{Tier} e.g., Licence-E5
CA-Exclude-{Purpose}
Role-{RoleName}
{Purpose} All, Readers, Writers, Admins, Why it exists (optional for simple groups
Users, Analysts, etc. where scope is sufficient)
Examples (groups NE will create):
SG-All-Staff Security group, organization-wide
SG-Engineering-All Security group, Engineering department
SG-Manchester-Staff Security group, Manchester site
DG-Finance-All Dynamic group, Finance department
M365-Project-Safety-Compliance M365 group, Safety project collaboration
SG-App-Sentinel-Readers Security group, Sentinel application readers
SG-App-CAD-Users Security group, CAD application users
SG-Licence-E5 Security group, E5 license assignment
SG-Licence-E3 Security group, E3 license assignment
SG-CA-Exclude-BreakGlass Security group, CA break-glass exclusion
SG-CA-Exclude-LegacyApp Security group, CA legacy app exclusion
SG-Role-ExchangeAdmin Security group, Exchange Administrator role
DL-Engineering-Announce Distribution list, Engineering announcementsEdge cases the taxonomy handles:
A cross-department project group: M365-Project-Cloud-Migration — the "Project" scope indicates it's cross-department. The naming convention doesn't encode which departments participate (that's membership, not naming).
A temporary group created for a specific event: SG-Temp-Office-Move-2026 — the "Temp" scope signals this group has a planned end date and should be reviewed/deleted after the event. MSA12's governance architecture uses this prefix to schedule automatic expiration reviews.
A group created by self-service (user-created M365 group for Teams): the naming policy (configured below) automatically adds a prefix or suffix, ensuring even user-created groups are identifiable.
CA policies: CA-{Persona}-{Action}-{Scope}
Component Values What it tells you
--------- ---------------------------------- ------------------------------------
CA- Fixed prefix (all CA policies) Identifies this as a CA policy
{Persona} All, Admins, Guest, WL (workload), Who the policy targets
BG (break-glass — exclusion)
{Action} RequireMFA, RequirePhishResistMFA, What the policy requires or blocks
RequireCompliantDevice,
BlockLegacyAuth, BlockHighRisk,
BlockCountry, RequireSignInFreq
{Scope} AllApps, AzurePortal, Exchange, Where the policy applies
SharePoint, SpecificApps
the organization's current 6 policies renamed:
"Block legacy auth" → CA-All-BlockLegacyAuth-AllApps
"Require MFA for admins" → CA-Admins-RequireMFA-AllApps
"MFA for all users" → CA-All-RequireMFA-AllApps (currently report-only)
"Test - guest MFA" → DELETE (test policy, disabled, no production value)
"Block risky sign-ins" → CA-All-BlockHighRisk-AllApps
"New policy" → DELETE (unnamed, disabled, no configuration)Governance query enabled by the convention:
# "Which CA policies target admin users?"
Get-MgIdentityConditionalAccessPolicy |
Where-Object { $_.DisplayName -match '^CA-Admins' } |
Select-Object DisplayName, StateThis query is impossible with the current naming. It takes zero seconds with the convention.
Application registrations: App-{Environment}-{Purpose}-{Owner}
Component Values What it tells you
----------- ---------------------------- ---------------------------------
App- Fixed prefix Identifies this as an app registration
{Environment} Prod, Dev, Test, Staging Is this production or development?
{Purpose} Descriptive name (CRM, HR-Sync, What does the application do?
Sentinel, Helpdesk-Bot, etc.)
{Owner} IT, HR, Engineering, Security, Which team is responsible?
External
the organization's current registrations renamed:
"CRM Integration" → App-Prod-CRM-Integration-IT
"ERP Connector" → App-Prod-ERP-Connector-IT
"HR Data Sync" → App-Prod-HR-Data-Sync-HR
"Reporting Dashboard" → App-Dev-Reporting-Dashboard-IT (or delete if abandoned)
"IT Helpdesk Bot" → App-Prod-Helpdesk-Bot-IT
"Visitor Management" → App-Prod-Visitor-Mgmt-FacilitiesWhen MSA1.8's stale app detection finds "App-Prod-HR-Data-Sync-HR" with expired credentials, you know immediately: it's production (impact if broken), it syncs HR data (sensitive), and HR owns it (contact them to investigate). "HR Data Sync" tells you what it does but not who to contact or whether it's production.
AU naming convention
Established in MSA1.6 and validated here:
# Verify AU naming compliance (should all follow NE-{Scope}-{Name})
$aus = Get-MgDirectoryAdministrativeUnit -All
if ($aus.Count -gt 0) {
$aus | ForEach-Object {
$compliant = if ($_.DisplayName -match '^NE-') { "✓" } else { "❌" }
Write-Host " $compliant $($_.DisplayName)"
}
} else {
Write-Host " No AUs exist yet — convention will be applied when MSA1.6 design is implemented"
}Enforcing the convention — configuration and limitations
Entra ID provides a built-in naming policy for M365 groups. It does not provide naming enforcement for security groups, CA policies, or app registrations. Understanding this enforcement gap is essential — it determines where you rely on automation and where you rely on process discipline.
Group naming policy (M365 groups)
The naming policy is configured through the Group.Unified directory settings template. It supports two features: automatic prefix/suffix injection (the system adds strings or attribute values to the group name at creation time) and custom blocked words (the system rejects group names containing specific words).
Connect-MgGraph -Scopes "Directory.ReadWrite.All"
# Check if a naming policy exists
$settings = Get-MgBetaDirectorySetting |
Where-Object { $_.DisplayName -eq "Group.Unified" }
if ($settings) {
Write-Host "Existing naming policy:"
$settings.Values | Where-Object { $_.Name -in 'PrefixSuffixNamingRequirement','CustomBlockedWordsList' } |
Select-Object Name, Value | Format-Table -AutoSize
} else {
Write-Host "No naming policy configured"
Write-Host ""
# Create the naming policy
$template = Get-MgBetaDirectorySettingTemplate |
Where-Object { $_.DisplayName -eq "Group.Unified" }
$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 "Naming policy created:"
Write-Host " Suffix: -[Department] (auto-appended from creator's Department attribute)"
Write-Host " Blocked words: test, temp, old, delete, copy, backup, dummy, sample, fake, draft"
}The PrefixSuffixNamingRequirement value [GroupName]-[Department] means when a user creates an M365 group named "Project Alpha," the system appends their Department attribute: "Project Alpha-Engineering." The [Department] is a dynamic attribute — it reads the creating user's Department value from their Entra ID profile.
Supported dynamic attributes: [Department], [Company], [Office], [StateOrProvince], [CountryOrRegion], [Title]. Maximum total length including prefix + group name + suffix: 63 characters.
The CustomBlockedWordsList prevents groups with names containing "test," "temp," "old," "delete," "copy," or "backup" from being created. This is a security control: test groups in production accumulate permissions that nobody reviews, temporary groups become permanent, and "copy of" groups create confusion about which group is authoritative.
Entra Admin Center
Configure naming policy:
Identity → Groups → All groups → Naming policy
The Group name policy tab configures prefix/suffix rules — choose between Attribute (dynamic, e.g., [Department]) and String (fixed text). Multiple prefixes and suffixes can be combined.
The Blocked words tab accepts a CSV upload (up to 5,000 words). Words are case-insensitive. Download the current list before uploading additions — the upload replaces the existing list, it doesn't append.
Critical limitation: The naming policy applies to M365 groups created through Teams, Outlook, SharePoint, Planner, and the admin center. It does NOT apply to security groups created by administrators via PowerShell, Graph API, or the Entra admin center "New group" flow with group type "Security." Security group naming must be enforced through process and audit.
Enforcement gap — security groups
The naming policy limitation is architecturally significant because security groups are the most numerous type (118 of 214 at NE, 55%) and the type most critical for access control. If an admin creates a security group named "temp admins" or "backup group 2" via PowerShell, the naming policy doesn't block it.
The compensating control is a compliance auditing script that runs on a schedule (weekly or daily) and reports naming violations:
# Naming compliance audit — run weekly
$prefixPattern = '^(SG-|DG-|DL-|M365-|Team-|SP-|App-|CA-|Role-|Licence-)'
$blockedWords = @('test','temp','old','delete','copy','backup','dummy','sample','fake','draft')
$groups = Get-MgGroup -All -Property DisplayName, GroupTypes, CreatedDateTime
$violations = $groups | Where-Object {
$name = $_.DisplayName
# Check 1: No prefix
$noPrefix = $name -notmatch $prefixPattern
# Check 2: Contains blocked word
$hasBlocked = $blockedWords | Where-Object { $name -match $_ }
$noPrefix -or $hasBlocked
} | Select-Object DisplayName,
@{N='Issue';E={
$issues = @()
if ($_.DisplayName -notmatch $prefixPattern) { $issues += "No prefix" }
$blockedWords | ForEach-Object {
if ($_.DisplayName -match $_) { $issues += "Blocked word: $_" }
}
$issues -join "; "
}},
@{N='Created';E={$_.CreatedDateTime.ToString("yyyy-MM-dd")}}
Write-Host "=== Naming Compliance Audit ==="
Write-Host "Total groups: $($groups.Count)"
Write-Host "Compliant: $($groups.Count - $violations.Count) ($([math]::Round(($groups.Count-$violations.Count)/$groups.Count*100))%)"
Write-Host "Violations: $($violations.Count) ($([math]::Round($violations.Count/$groups.Count*100))%)"
Write-Host ""
$violations | Sort-Object Created | Format-Table -AutoSizeThis script becomes a scheduled task in MSA12's governance architecture. Every violation generates a notification to the group creator's manager or the identity team. Over time, the compliance percentage should increase from 41% toward 95%+ as existing groups are renamed and new groups follow the convention.
Renaming existing groups — impact assessment and safe execution
Renaming 127 non-compliant groups is a governance project, not a single script run. Before renaming any group, you need to understand what references it.
# Impact assessment for a specific group rename
$groupName = "SOC Analysts"
$group = Get-MgGroup -Filter "displayName eq '$groupName'"
Write-Host "Group: $groupName (ID: $($group.Id))"
Write-Host ""
# Check CA policy references (CA policies use group IDs, not names)
$caPolicies = Get-MgIdentityConditionalAccessPolicy -All
$caReferences = $caPolicies | Where-Object {
$conditions = $_.Conditions
$group.Id -in $conditions.Users.IncludeGroups -or
$group.Id -in $conditions.Users.ExcludeGroups
}
Write-Host "CA policies referencing this group: $($caReferences.Count)"
$caReferences | ForEach-Object { Write-Host " $($_.DisplayName) (uses group ID — safe to rename)" }
Write-Host ""
# Check member count
$members = (Get-MgGroupMember -GroupId $group.Id -All).Count
Write-Host "Members: $members"
Write-Host ""
# Safe to rename — CA policies reference by ID, not name
Write-Host "Rename safe: CA policies use group ID internally. Renaming doesn't break policy targeting."The safe rename process:
# Rename the group
Update-MgGroup -GroupId $group.Id -DisplayName "SG-SOC-Analysts"
Write-Host "Renamed: '$groupName' → 'SG-SOC-Analysts'"
# Verify
(Get-MgGroup -GroupId $group.Id).DisplayNameCA policies reference groups by ID (GUID), not by display name. Renaming a group that's targeted by a CA policy is safe — the policy continues targeting the group by its ID. The same applies to application assignments, license group assignments, and PIM role assignments — all reference group IDs internally.
The exceptions where renaming can break things: PowerShell scripts that filter groups by display name (Where-Object { $_.DisplayName -eq "SOC Analysts" }), Azure Automation runbooks with hardcoded group names, and user expectations (people search for the old name in Teams or Outlook). Document these dependencies before bulk renaming and update scripts and runbooks as part of the migration.
ADR-MSA1-003: Naming convention standard
Context. the tenant has 214 groups (41% compliant), 6 CA policies (0% compliant), and 34 app registrations (0% compliant). No naming policy is configured. The lack of a convention makes programmatic governance impossible — every audit, review, and automation requires manual object inspection.
Decision. Establish the NE naming convention for all Entra ID objects:
- Groups:
{Type}-{Scope}-{Purpose}with type prefixes SG-, M365-, DL-, DG- - CA policies:
CA-{Persona}-{Action}-{Scope} - App registrations:
App-{Environment}-{Purpose}-{Owner} - Administrative Units:
NE-{Scope}-{Name}(established in MSA1.6) - Configure M365 group naming policy with
[GroupName]-[Department]suffix and blocked words - Enforce security group naming through weekly compliance audit script
- Migrate existing objects to the convention in batches (MSA12 designs the timeline)
Consequences. Enables programmatic governance queries (Get-MgGroup -Filter "startswith(displayName, 'SG-Engineering')"). Enables lifecycle workflow automation that operates on group name patterns. Requires batch migration of 127 non-compliant groups (safe: CA policies reference by ID, not name). Requires audit script for security groups (naming policy doesn't enforce on admin-created security groups). Requires 2 CA policy deletions (test/unnamed disabled policies).
Communication. "We're standardising how we name groups, policies, and applications so that automated security reviews work reliably. You'll see groups renamed over the next quarter — Teams and SharePoint site names won't change, only the underlying Entra group display name."
Before moving on, verify your understanding: Run the naming compliance audit against your tenant. What percentage of groups follow a structured prefix? Identify the three worst violations and specify what each should be renamed to. Marcus needs to find all groups related to Engineering. Write the Graph API query that answers this with the naming convention. Explain why this query fails without the convention.
Reusable script — the commands from this sub assembled for operational use:
Copy the complete naming taxonomy and ADR-MSA1-003 into your architecture package at 01-identity/naming-convention-standard.md. Save the compliance audit script at 01-identity/scripts/naming-compliance-audit.ps1.
This standard is referenced by every subsequent module. Establishing it now means every object created from MSA2 onward follows the convention from day one. The audit script runs weekly to detect drift.
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.