Identify shadow mailboxs (InvalidLicenseException)

Identify shadow mailboxs (InvalidLicenseException)

When we perform migrations, the customer is responsible for choosing the objects which should migrate (for this blog example; users, resources, shared mail). Often this also includes choosing the AD or AAD identities they want to sync from one place to another too, so that target identities can be provisioned and mailbox’s created.

We ask the customer to do this because for each resource there might be migration tooling license consumed increasing costs, furthermore leavers might not be well identified. There are lots of other reasons, for example, streamlining or consolidating your target identity but this is not the blog for that. Certainly taking “everything” is something we never come across once we drill into the details of the project, there are always accounts surplus to requirements.

A customer recently identified everything in scope for migration by listing everybody with a mailbox using a query like this.

(RecipientTypeDetails -eq 'UserMailbox') -and

(UserType -eq 'Member') -and

(IsLicensed -eq $True)

What they didn’t realise is that M365 deploys mailbox’s for SKUs like Streams, Teams Exploratory, Power BI and Flows to allow those features to function. For example, one to one chat in Team is stored in the UserMailbox, so a mailbox is required if a Member only has that feature enabled. These Members are now considered licensed with a mailbox. However, they do not have a mailbox which they can officially consume and access.

Any migration tools like MigrationWiz, or simply accessing the mailbox over OWA you will receive errors like the following

Via OWA:-

UTC Date:
Client Id:
Session Id:
Client Version: 20221021015.08
BootResult: configuration
err: Microsoft.Exchange.Data.Storage.InvalidLicenseException
esrc: StartupData
et: ServerError
estack: Error: 500
at S (https://outlook.office.com/mail/xxx@xxx.onmicrosoft.com/:10:265754)
at https://outlook.office.com/mail/xxx@xxx.onmicrosoft.com/:10:274159
st: 500
ehk: X-OWA-Error
efe: DB8P191CA0015, LO4P123CA0461
ebe: DB3PR0402MB3804
ewsver: 15.20.5791.22
emsg: InvalidLicenseError

And for a tool like MigrationWiz it will be something like this

Your migration failed while checking destination credentials. An internal server error occurred. The operation failed., Mailbox 'xxx' doesn't have a valid license.
Error: ErrorInternalServerError
Detail: InnerErrorMessageText The license is invalid for this mailbox.
Detail: InnerErrorResponseCode ErrorInvalidLicense
Detail: InnerErrorDescriptiveLinkKey 0

So we cannot migrate the data.

I wanted to set about identifying these circumstances so I could feed back to the customer which accounts probably shouldn’t be included in the migration of data.

First I need a list of the different SKUs and Exchange Components involved

$KeySKUsRegex = "ENTERPRISEPACK|ENTERPRISEPREMIUM|SPE_E3|STANDARDPACK"

$ExchComponentRegex = "EXCHANGE_S_FOUNDATION|EXCHANGE_S_ENTERPRISE|EXCHANGE_S_STANDARD|EXCHANGE_S_ESSENTIALS"

$ExchangeLicenses="EXCHANGESTANDARD|EXCHANGEENTERPRISE"

I then needed to collect all the tenant data with all the correct licensing information. The best place for this is Get-MSOLUSER

$users = Get-MsolUser -All

I also need to be able to identify if the users have a mailbox, I wanted to put this into a hash table so it was easy to lookup the corresponding object in EOL using the ObjectID from Get-Msoluser, just incase there were sync issues.

$recipients = Get-EXORecipient -ResultSize unlimited | Group-Object -AsHashtable -Property ExternalDirectoryObjectId

Now the query to drill into the accounts.

$ShadowMember = $users | ? { (

!(($_.Licenses.AccountSkuId -match $KeySKUsRegex) -or ($_.Licenses.AccountSkuId -MATCH $ExchangeLicenses)) -AND

($_.Licenses.ServiceStatus.ServicePlan.ServiceName -match $ExchComponentRegex) -and

($recipients.$($_.objectID.guid).RecipientTypeDetails -eq 'UserMailbox')) }

What this code does is

  1. Make sure no core License exists in the user by looking at the AccountSkuId
  2. Also ensure that no Exchange license has been directly applied onto the user in AccountSkuId
  3. Now we need to verify that any exchange component has been enabled on the Member by checking the list of enabled components in ServiceName
  4. Finally, for belts and braces, ensure that the object we’re looking at really is a UserMailbox and not something like a shared mailbox with a license applied so that it can go above 50 GB.

There are some optimisations which could be done, like consolidating $KeySKUsRegex and $ExchangeLicenses, but Im using them for other queries, so I didnt want to here, it also serves as a good step by step explaination of what we’re trying to achieve.

The next question is, these users have a mailbox for a reason, to enable features in Teams/Flow etc, so should we be licensing them and migrating any data anyway?