Today’s script exercise covers a simple PowerShell script to generate a report of all email addresses (aliases) within your Microsoft 365 tenant. This is an updated version of the similar script we examined in this article.
Compared to the last time we explored this scenario, several new RecipientTypeDetails values have been added to Exchange Online, including: SubstrateGroup, SubstrateADGroup, WorkspaceMailbox, SharedWithMailUser, ServicePrinciple, BlobShard, DeskMailbox, SubstrateTenantRecipient. Out of these, only Workspace mailboxes are “user facing”, i.e. represent a functionality we can play with. While Desk mailboxes should represent a similar use case, the corresponding Desk value for the (mailbox) -Type parameter is currently not available. In effect, both Workspace and desk mailboxes represent a special type of Room mailboxes (with the corresponding ResourceType value). While we cannot directly filter by either the WorkspaceMailbox nor DeskMailbox value, luckily, the RoomMailbox value covers both of those types.
Out of the above list, the only value we can actually use against the –RecipientTypeDetails parameter is the SharedWithMailUser one. There’s zero documents online detailing on this type of recipient however, and at this point I’m not sure whether it’s actually in use within the service. Nevertheless, we can include it in the script, and threat it the same as “regular” MailUser objects.
Another thing worth mentioning is the advent of plus addressing in Exchange Online. Plus addresses however are dealt with on the client and transport layer, and there’s no matching email address stamped on the mailbox object. That said, using the plus sign (“+“) as part of an email address is perfectly acceptable, so Exchange must actually verify the existence of such aliases when handling message delivery. In other words, you might have an object with email@example.com alias, and another one with firstname.lastname@example.org. In such scenarios, the former takes precedence and Exchange will always try to deliver the message to the matching mailbox. If on the other hand no email@example.com address is found within the directory, Exchange will try to deliver the message to firstname.lastname@example.org instead. This doesn’t really impact the current script, but I mention it as you might start seeing some addresses featuring the “+” character. Rest assured, those have been added by an admin and do not represent any plus address the user has conjured himself – we have no visibility on these.
The last thing to consider is the newly released Exchange Online “V2” PowerShell, which is now a requirement for running the script and brings improvements in performance and handling the connectivity part (via the Check-Connectivity helper function). Apart from that, the script features a single other function that contains all the logic, namely Get-EmailAddressesInventory, which you can call directly by dot-sourcing the script, if needed. Said function, and the script itself, supports a rather large set of parameters, as follows:
- IncludeUserMailboxes – specify whether to include User mailboxes in the output
- IncludeSharedMailboxes – specify whether to include Shared mailboxes in the output
- IncludeRoomMailboxes – specify whether to include Room, Equipment and Scheduling mailboxes in the output
- IncludeGroupMailboxes – specify whether to include Microsoft 365 Group mailboxes in the output
- IncludeDGs – specify whether to include Distribution Groups, Dynamic Distribution Groups, Room Lists and Mail-enabled Security Groups in the output
- IncludeMailUsers – specify whether to include Mail Users, Guest Mail Users and SharedWIth Mail Users in the output
- IncludeMailContacts – specify whether to include Mail Contacts in the output
- IncludeAll – specify whether to return all supported recipient types in the output
- IncludeSIPAliases – specify whether to include SIP/EUM aliases in the output
- IncludeSPOAliases – specify whether to include SPO aliases in the output
- CondensedOutput – specify whether to write the output in condensed format
The purpose of all these parameters is to give you some granularity, and produce a somewhat cleaner output. For example, if you only care about user mailboxes, run the script with the corresponding –IncludeUserMailboxes switch:
This is in fact the default behavior of the script, when no other parameters are passed. If instead you want to include every supported recipient type, run the script with the –IncludeAll parameter. This is the only to include some more exotic recipient types in the output, as you actually have to pass the corresponding value to the –RecipientTypeDetails parameter when executing the Get-ExORecipient cmdlet. Whereas back in the day this workaround applied to Microsoft 365 Group objects, those are now included by default, so the above remark currently only applies to Public folder mailboxes (not the actual mail-enabled public folders themselves).
Another set of parameters, –IncludeSIPAliases and –IncludeSPOAliases, determines whether the corresponding alias type is included in the output, again in an effort to produce a cleaner output. By default such entries will be omitted, if you want them included make sure to specify the corresponding parameter(s). The below example will five you the fullest possible picture:
.\O365_aliases_inventory.ps1 -IncludeAll -IncludeSIPAliases -IncludeSPOAliases
Lastly, we have the –CondensedOutput switch, which governs whether the output format uses one line per recipient object (with columns Recipient, Primary SMTP address, UPN, Recipient type, EmailAliases, SIP Aliases, SPO Aliases and External Email Address), or one line for each alias (so multiple lines per recipient object, with columns Recipient, Primary SMTP address, UPN, Recipient type and Aliases). The default is the latter, which allows for easier filtering of the exported CSV file. Sample output is shown on the screenshot below:
As with most of the scripts I author, the output will also be stored to a global variable, $varAliases, which allows you to perform some manipulations before exporting, or use it as the input for another script/cmdlet. And that’s all I can think of with regards to the script, it’s a simple one after all. Do let me know if you have any issues or suggestions. You can download the updated version of the script from my GitHub repo here:
One type of addresses that the script does not cover is the Teams channel email addresses, though technically those are not authored in your own tenant. In any case, should you want to cover such addresses as well, I got you covered.