Open the group list in a tenant that has been running for a few years, and this is roughly what you find:
AAD_LIC_Subcontractors_E5 AAD_LIC_DYN_AADP2 SG-User-AppConsentAdmin sg-groupreporting-prod-admin AAD_Intune_UA_Wallpaper_Settings_TEST AAD_M365_AppAdmin_SecurityBaseline MESG-MonitorM365License sg_someones_testgroup
Each one made sense to the person who made it, on the day they made it. (Some of them are mine. I’m not going to tell you which.) Nobody did anything wrong. Names just pile up, from a lot of hands, over a lot of years, with no one owning the list.
The cost shows up later, always at the worst moment. You can’t tell from these names what they grant, who they’re for, or whether anyone still uses them. So every access review turns into detective work, every audit into educated guessing, and offboarding quietly leaves licenses behind. Nothing breaks loudly. The tenant just slowly stops being something you can trust.
That’s the problem, and I’ll leave it there. The rest of this post is the fix, and the fix is mostly about deciding what a name is actually for.
In a hurry? The whole standard fits on one page — cheat sheet on GitHub. The rest of this post is the why.
What a name is for
One goal, before any convention:
You should be able to read what the access is from the name, without opening anything.
That’s the whole bar. Not who approved it, not how long it lasts, not which policy granted it. Just: what is this, and who’s it for. Because a name is only one of three layers, and it only has to carry one of them:
- The name says what the access is. The intent.
- The description says what’s in it right now, and why. The content.
- Governance says who got it, when, and for how long. The request and approval trail.
Most naming pain comes from forcing the name to do all three. It can’t. The moment a name tries to carry the content or the assignment, it starts to lie, because those change and the name doesn’t follow.
The idea that makes the rest easy
Here’s the move everything else hangs on:
An access package is a stable identity. The resources inside it are swappable content. The name describes the identity, never the content.

An access package is a thing you keep. It carries every assignment you’ve approved, every review you’ve run, all the history. You don’t want to throw that away because a license changed.
The resource it points to is the opposite. It’s disposable. Today the package grants a license group; if the need changes, you swap the group, and the package, with all its approved assignments, stays exactly where it is.
Licenses are the clearest case, because they change the most. Say License - Baseline 5 grants a group that hands out Microsoft 365 E5:
License - Baseline 5 → grants LIC-M365-E5
Now the company decides that baseline should move up to Microsoft 365 E7. Nobody changed roles. The whole baseline is being upgraded, for everyone on it.
You could reissue and re-approve every assignment. You don’t. Instead:
- Create a new group,
LIC-M365-E7, and assign the E7 license to it. - Open the access package, remove the old resource, add the new one.
License - Baseline 5 → grants LIC-M365-E7
The access package never changed. Same name, same level, same approvals, same policies. Everyone who already had Baseline 5 is added to the new group automatically and picks up E7, with no new requests and nothing to migrate. You swapped the license underneath the identity, and the identity didn’t notice.
That’s why the package is License - Baseline 5 and not License - E5. The name describes the intent (“this is a baseline license set”), and the intent doesn’t change. The product (E5, E7, Entra ID P2, a mailbox, whatever it grows into) lives on the resource, where it’s cheap to swap. Put the product in the package name and you’ve welded the content to the identity. Now you’re renaming packages on every change, or living with names that lie.
The number is just a label. Baseline 1, Baseline 2: they don’t rank anything. 2 isn’t “better” than 1, it’s a different baseline. What each one actually contains is what the description field is for. The name tells you which. The description tells you what.
And the group is only one kind of resource. It could as easily be an app role, a SharePoint site, anything an access package can grant. The pattern doesn’t care: stable package, swappable resource. Licenses are just the example that proves it, because they’re what you’ll swap most often.
What the name carries, and what it doesn’t
If the name only carries intent, here’s what that means in practice.
Persona, only when it’s part of the access. Hand the same access to a different persona; is it still the same access? A baseline license passes that test: an E5 is an E5 whether it lands on an employee or a subcontractor. So persona stays out of the name, and which persona gets it is handled in the policies. But an app bundle built specifically for one persona genuinely differs per persona, so there it belongs: App - Employee - SAP User Access.
Never the assignment. Who can request it, who approves, how long it lasts: that’s policy. It differs per persona and changes over time. The name is not a second copy of a setting.
Never the object type. No SG, AAD, M365, DL bolted on the front. The directory already knows what kind of object it is. Saying what something does, like LIC or PIM, is fine. That’s function, not class.
Keep the product off the package name when you can. A specific SKU like F3 or E5 usually belongs on the resource, not on the stable package, so you can swap it without touching the package. On the resource it’s welcome: that’s the swappable layer, and LIC-M365-E5 should say exactly what it is. The exception is when the product genuinely is the intent and won’t be swapped out from under the name. Then it can earn its place. The test is the same as always: will this still be true after the next change? If the answer is “only until we upgrade the license”, keep it on the resource.
One structure, one separator, every time. Which separator doesn’t matter: hyphen, space, whatever you like. Pick one and stick to it. The only mistake is switching halfway. LIC-M365-E5, LIC_M365_F3, and LIC M365 P2 are three different strings to a search box, and that’s the whole problem.
What it looks like
Access packages. What it is, persona (when it’s part of the access), then what specifically:
License - Baseline 1 License - Baseline 2 App - Employee - SAP User Access Role - ICT - User Administration Role - ICT - Identity Governance

Resources, the swappable content. Short prefix, what it carries, no persona:
LIC-M365-E5 LIC-M365-F3 PIM-EntraGovernanceAdmin
The same resource can sit behind more than one package. An employee baseline and a subcontractor upgrade can land in the same license group; a department and a project can need the same site. The access is identical, only the route in differs, so the resource is named for what it grants, never for who routed to it. Entra even keeps a user in the group until the last package that grants it expires; it reference-counts the routes for you.
Catalogs, the persona container:
Identity - Employee Identity - Subcontractor
Lifecycle workflows. Persona, event, then the timing as an ISO 8601 duration offset from the trigger date:
Subcontractor - Pre-Onboard (P7D before) Subcontractor - Activate (P0D) Subcontractor - License Cleanup (P14D after)
P7D is seven days, P0D is the day itself. A duration carries no direction on its own, so before or after says which side of the trigger date it falls. Read top to bottom and you get a subcontractor’s whole life in the tenant, in order, without opening a single one.
What gets better
This was never about a tidy list. It’s about the days you’ll actually spend in here.
Reviews and audits stop being archaeology. The name in the log line tells you what the access was, on its own, even for an object someone deleted last quarter.
Incidents get faster. A name shows up in a log mid-incident, you read it, you move on, instead of clicking into the portal to decode a string while the clock runs. Need who approved it and when? That’s one step away in governance, on purpose.
Licenses change without drama. Swap the resource, keep the package, keep every approved assignment. The thing that used to feel like a migration becomes an afternoon.
The sprawl stops. When names carry intent and resources carry content, you stop minting a near-duplicate object every time a detail shifts. Fewer objects, clearer logs, less to open.
Closing
A naming standard sounds like the smallest decision in an Entra ID design. It gets agreed in five minutes and forgotten the first time someone’s in a hurry.
But the name is the layer everything else reads from. Automation targets it, audits filter on it, the next admin trusts it. Get it wrong and you don’t find out for a year, at the access review, which is exactly where I found out.
The whole thing comes down to one habit: name the intent, not the content. The package is what you keep. The resource is what you swap. The name should describe the part that doesn’t move.
So before you name the next thing, ask:
Could someone tell what this is from the name alone, without opening it, and without already knowing what I know?
If yes, it’s doing its job. If not, you haven’t named it. You’ve just labeled it.
I keep the condensed version as a one-page cheat sheet on GitHub — bookmark that one; it’s the part you’ll actually reach for.
Discover more from Agder in the cloud
Subscribe to get the latest posts sent to your email.

