Getting sub-organization per-license counts in a shared tenant environment

5/5 - (3 votes)

If you’ve ever been involved in tenant-to-tenant migrations, getting an accounting for which users are consuming which licenses is part of the job. When you’re working with large shared infrastructures with lots of different SKUs and license assignments, it goes from being a chore to a full-on headache sometimes.

Background

One of the challenges with large organizations (such as governments or multi-national corporations acting as umbrellas with subsidiaries or business units) is determining how tenants will be laid out.

Many organizations go with the “my data, my tenant” approach. Everyone wants their own ability to set and control policy; and in some cases, Microsoft 365 tenants have some settings that can only be implemented at the top level, potentially giving rise to risks of organizations being impacted by configuration actions taken by administrators in other organizations.

Some organizations implement a “shared tenant” model, where all sub-organizations or business units house all of their resources (users, groups, computers, contacts, and whatever other objects they have in the directory) in a centralized Azure AD instance. In these cases, organizations may have multiple licensing arrangements and assignments. Since SKUs are pooled across the tenant, anyone could literally have any license.

So, when trying to migrate a business unit or group out of a tenant into their own space, how can you quickly determine what licenses are needed int the target tenant?

Solution

This wouldn’t be my blog if there wasn’t a code snippet.

In this case, I’m going to return a filtered list of users with the legacy Get-MsolUser cmdlet (though there are other, more difficult ways with the Azure AD cmdlets or the new-and-cumbersome Microsoft Graph cmdlets). As long as the MSOnline cmdlets live, I’ll keep using them to make my job easier.

First, get your list of users:

$Users = Get-MsolUser -All -DomainName <vanitydomain.com> | Select UserPrincipalName,Licenses

If you can’t parse your users out like that, you’ll need to come up with a different filter (such as department or company name).  Get-MsolUser doesn’t support the Filter parameter, so you may have to fidget a bit to find a query that works for you (or use a different cmdlet).

One of the things you’ll notice is that Licenses is a multi-valued attribute, which makes sense–users can have more than one license assignment.

For example:

PS C:\> $Users

UserPrincipalName      Licenses
-----------------      --------
user001@govdomain.gov  {govtenant:Microsoft_365 G5_Security_for_GCC, govtenant:EQUIVIO_ANALYTICS_GOV, govtenant:EXCHANGEENTERPRISE_GOV}
user992@govdomain.gov  {govtenant:Microsoft_365 G5_Security_for_GCC, govtenant:EQUIVIO_ANALYTICS_GOV, govtenant:EXCHANGEENTERPRISE_GOV}
user003@govdomain.gov  {govtenant:EXCHANGEENTERPRISE_GOV}
user004@govdomain.gov  {govtenant:Microsoft_365 G5_Security_for_GCC, govtenant:EQUIVIO_ANALYTICS_GOV, govtenant:STANDARDPACK_GOV}
user005@govdomain.gov  {govtenant:M365_G5_GCC}

Now, you could very well count those up by hand on a sheet of paper, but that’s terrible and I might question your sanity–or at least your commitment to looking for a Rube Goldberg solution and turning this into a scripting task.

We’ll use this one-liner to select the Licenses attribute’s AccountSkuId value (where the name of the license is displayed), loop through each individual item in the collection, and then group them by the Name attribute. And, since we want to clean up the output, we’ll use a calculated value to split the govtenant:SKU at the “:” and just return the second object.

$Users | % { $_.Licenses.AccountSkuId } | Group-Object | Select Count,@{L="Name";E={$_.Name.Split(":")[1]}}
PS C:\> $Users | % { $_.Licenses.AccountSkuId } | Group-Object | Select Count,@{L="Name";E={$_.Name.Split(":")[1]}}

Count Name
----- ----
   49 Microsoft_365 G5_Security_for_GCC
   42 EQUIVIO_ANALYTICS_GOV
   41 EXCHANGEENTERPRISE_GOV
   31 STANDARDPACK_GOV
  277 M365_G5_GCC
   16 PROJECTPROFESSIONAL_GOV
   59 POWERBI_PRO_GOV
   14 POWERAUTOMATE_ATTENDED_RPA_GCC
    7 MCOMEETADV_GOV
   17 M365_G3_GOV
   26 VISIOCLIENT_GOV
    1 PBI_PREMIUM_PER_USER_GCC
    2 PROJECTESSENTIALS_GOV
    3 MEETING_ROOM_GOV

While this particular tenant only has about 325 *real* users, you can see that the licensing assignments are all over the board. There are probably better ways to handle this with some smart license consolidation, but I’ll leave that for the licensing gurus out there.