Office 365: Script to get detailed report of assigned licenses

BLOG UPDATE: April 1st 2021

Hi everyone. I wrote this blog post many years ago, and throughout the years this has been a blockbuster in terms of visits. Thank you for that.

I think that highlights the growing need companies have, to assess their Microsoft 365 portfolio. That of course comes from a gap in the native portal and the native reporting capabilities.

I decided to update this blog post and let you all know about the BitTitan Voleer IT Automation Toolbox. Those who know me, know that I work for BitTitan, but this is not a “sponsored” blog post. You can register for free and it literally takes you 5 seconds to fill in the below, which is all the information you need to give, to register and trial it for 30 days.


Once you register you can login and either search in the library for “Microsoft 365 License Usage And Optimization Assessment” or go directly to it, by clicking here.

You can then follow these simple steps to execute the assessment:

  1. Read the instructions and click on “Launch” on the right hand side, once you select the Workspace (Sandbox is the default)
  2. Authorize Microsoft Graph access to your tenant, by clicking the available links and entering the code provided. Click “Validate” when done.
  3. In “Configure” you can configure filters to assess just part of your Organization. The filters can be done by dropdowns, string or using User Principal Names. Common filters you can use include country, account log in state or Department, amongst others. Leave blank to assess the entire tenant.
  4. Also in “Configure” you can select to email the report from a Voleer email address, or from one that you would put username and password for. You then select to which address is the report being sent to and password protect it.
  5. Once all of the above is completed, click “Execute”

Once the execution is complete you will get an email, with an attached CSV with multiple tabs, with all the information you need. See below an example of the assessment Dashboard.

MicrosoftTeams-image (3)

You can save the template configuration, to make future executions easier and you can also schedule it. Those are 2 huge benefits that add to the fact that you have the awesome feature of filtering the assessment (i.e assess licensing for login disabled users only). Top that with not having to handle PowerShell scripting and I think you have enough reasons to go check Voleer out.

Voleer has many other templates in the library, that you can check out and run during your free trial.

Any questions let me know and, if you want to run it the “old way”, keep reading. I have the PowerShell “one liners” below that will help you create the reports of your licensed users.


It’s very common to see Office 365 administrators asking in the community “How can I get a detailed report of the licenses i have assigned on Office 365?

Well it will depend on how detailed you want the report. I’ll detail here two solutions.

1 – Get a report of all licensed users and the AccountSKUId name

To run this report you need to open the Windows Azure Active Directory Module for Windows Powershell, and connect to Office 365. Once connected run the following cmdlet:

Get-MSOLUser -All | select userprincipalname,islicensed,{$_.Licenses.AccountSkuId}| Export-CSV c:\userlist.csv -NoTypeInformation

The above command lists ALL users and not just the ones that have a license. See the output CSV file below. There are ways of filtering the output (i.e export only licensed users), but i will keep this post simple. Let me know if you need something more elaborated.


2 – Get a detailed report of the licenses enabled for each user

One of the other requirements, is to know in detail, how many licenses per product do you have enabled, and which users have that license. If you want a detailed list with the users that have Lync Online, Exchange Online, Office Pro Plus (just to give three examples), or any other product that you have on your subscription, enabled or disabled, all you need to do is use the “Export a Licence reconciliation report from Office 365 using Powershell” script available on the Microsoft Gallery.

Again to run this report you need to open the Windows Azure Active Directory Module for Windows Powershell, and connect to Office 365.

Once connected to Office 365 browse into the directory where you saved the script, and run it.


The script will prompt you for the Office 365 administrator credentials, and run against all licensed users. By default the script creates a file named “Office_365_Licenses.csv” that will be created on the same directory where the script is. If you want, you can change it by editing the script. There’s also some other things you can change on the script, such as export all users and not just the licensed users, or use the existing credentials cached on your powershell session, instead of prompting you for credentials each time you run it. But again I will keep it simple for now, and if you want to change something on how the script works, let me know.

Now let’s have a look at the detailed output of the script.


Let’s now take the user Antonio Vargas as an example. He has all licenses assigned. Let’s see the view from the portal.


As you can see the Yammer licenses are assigned by default (hence the “PendingInput” state on the property exported to csv), and all other licenses are assigned, which matches with the success property on the csv. Now below let’s have a look at the user Calvin, which only has the Exchange Online license enabled (and the Yammer by default). All the other licenses are disabled.


Again when looking to the licenses that the user Calvin has assigned, via the Office 365 portal, it matches the csv file.

If you want, and because usually the output you will get is a very large csv file, you can use filtering at the csv level to get smaller lists depending on the license type you want the report on.

Any questions let me know, and happy reporting! 🙂


How to share the same email domain between two Exchange Online tenants for long term mail flow tenant to tenant coexistence [Part 1]

Note: Let me start with a quick note. This blog post was initially drafted to include multiple mail flow coexistence scenarios, between two Office 365 tenants. While I was writing the first scenario, I realized that it was so extensive that it didn’t make sense to put more scenarios in the same post. So stay tuned for more posts, each with its different scenario and some more focused on tenant to tenant enterprise migrations and mail flow during a coexistence period. Enjoy!

Some context

Microsoft tenant to tenant email migrations are very common in our days, and if some are small in terms of users, and the option is to just move them all at the same time, some are too big to have that approach.

Would you consider moving 50 thousand user mailboxes, in one single cut over batch, from one Exchange Online tenant to another? Probably not.

And there’s several reasons not to consider it, most, if not all of them having the user experience in mind. It’s not just about how many terabytes you can move per day, it’s also about re configuring Outlook profiles, re doing the Exchange partnership connection in your phone, all other Office 365 workloads, etc.

So when a cut over is not being considered, the coexistence between tenants comes into play. The biggest question is, can I leverage the same email domain in two tenants? And technically the answer is yes you can, but you’ll need an address rewriting service help you do it.

In a context of a migration, address rewriting is not all you need to have mail flow coexistence, but it’s the key component.

There are other components for you to consider, when doing tenant to tenant with coexistence, but this blog posts series will be focused on mail flow.

What do you need and why should you do it versus hosting it?

Lets address the requirements first. Assuming of course you have 2 tenants, the source and destination or in some cases just two tenants from which you want to send and receive email from, using a single SMTP domain, what you need next is something to do address rewriting (outbound email) and conditional forwarding or address rewriting (inbound email).

I will use the Microsoft Exchange Edge server. It’s a reliable service, that can be designed and implemented as high available and redundant, and it has a very easy to configure bidirectional address rewriting agent.

Note: before reading the rest of this blog post, I highly recommend that you click on the link above and read all details about how the Exchange Address Rewriting works.

Now lets address why should you do it yourself vs hosting it. In fact I think the decision is up to you, but there’s two things you should strongly consider:

  1. Do you want to pay a per user fee to host a service to either a migration software company or an email relay company, when you can do it yourself at much less cost?
  2. More importantly, will you allow a third party company into a crucial part of your email flow pipeline?

Those two questions are relevant, specially the second one. It involves SLAs and other super important things to consider, specially in the Enterprise space.

But if the answer to both questions above is yes, then you’re covered. If not, continue reading.

What will this blog post cover

This blog post will address a first scenario detailing mail flow simple coexistence with two tenants using one single email domain. I am labeling it as scenario 1, because this is the first blog post of a series that will cover other scenarios.

Scenario 1: Share a same email domain between two Exchange Online tenants


What are the requirements?

To set the scenario above you need the following:

  • Two Exchange Online tenants (of course!)
  • One unique vanity domain per Office 365 tenant, which in my cases is:
  • One vanity domain to be shared by the two tenants, which in my case is
  • At least one Microsoft Exchange Edge Server
  • An SSL certificate to encrypt mail flow between both Exchange Online tenants and the Microsoft Exchange Edge

Some important notes about the requirements:

  • my tenant vanity domains are sub domains of the shared vanity domain, but that is not a requirement. You can use different domains (i.e and in your configuration.
  • In this scenario all outbound and inbound mail flow goes via the Edge server. It is highly recommended that, for production purposes, you set up a high available and redundant multiple Edge server infrastructure.
  • My Edge Server is hosted in Azure, because it’s convenient for me to quickly set it up. You can host yours anywhere you want.
  • The SSL certificate and using TLS between the Exchange Online tenants and the Edge Server, is optional but recommended.

Outbound email from Office 365

In this scenario both tenants send all email via the Edge server. Optionally you can configure Conditional Mail Routing in Exchange Online, if you want just a group of users to route their email via the Edge. I am not going to detail that here, but please reach out if you need to elaborate more on that option.

Create the send connector in Exchange Online

First lets check how the connector in Office 365 is created. If you need any basic help on how to set up Exchange Online connectors, read this.

  • In the select mail flow scenario, select the source as “Office 365” and the destination as “Partner Organization”


  • Name the connector and turn it on


  • Select the destination email domains. I have selected * which means this connector applies to all external email domains


Note: Again here you can be more creative and configure a scenario where address rewriting (and more specifically here outbound email flow going via the Edge) only applies to specific destination domains – i.e you can rewrite addresses for your users but only when they email a specific partner company with a specific vanity domain

  • Enter the Public IP address of your Edge Server or load balancer in front of it.


  • Configure the connector to always use TLS when communicating to the Edge Server. Enter the subject name or the SAN of the certificate you will use (more on how to configure the certificate later)


And that’s it, your outbound connector is configured in your Exchange Online tenant. If you’re configuring this in a production environment, make sure you scope it for one test user first, until you’re 100% sure the Edge Server is configured properly and mail flow doesn’t have any issues.

Don’t forget to create the connector in both tenants.

Receive and Send connectors in the Edge Server(s)

Now that we addressed the Exchange Online connectors, lets look at the next hop in the mail flow pipeline, the Edge Server.

For this scenario to work properly, three things need to be created in the Edge Server:

  • Receive connector(s): This is key to accept email coming from EOP/Exchange Online
  • Address Rewrite Rule(s): To be applied by the inbound and outbound transport agents and rewrite addresses
  • Send connector(s): To send email outbound or inbound after the agent does its work

Lets address first the connectors.

Receive connector

There are multiple ways and scenarios to configure the receive connector, but ultimately here’s what you need to consider:

  • You should create a dedicated connector that accepts connections from EOP (Exchange Online Protection) only, by using the “remoteipranges” parameter when creating the connector
  • You should secure communications by adding TLS as an AuthMecanism and setting the “RequireTLS” flag to $true. You will also need to add the “TLSDomainCapabilities” and “TLSCertificateName” tags
  • If in your scenario, just like in mine, the Edge Servers are stand alone, you need to make the connector ‘ExternalAuthoritative’. This is key, because for address rewriting to be executed properly, the outbound emails should be considered internal email by the Edge Server. See more on how address rewriting works in this excellent article
  • Finally you need to make sure the connector accepts anonymous relay

Note: It is very important that you lock the connector down to a specific IP range and secure the communications with a certificate and TLS, since that connector will accept anonymous relay. Alternatively, if you have or can install an Edge Server in your existing Hybrid organization (and not stand alone like in this scenario), that will allow internal authenticated email, then you should opt for doing that.

Additional Note: I’ve added an additional method to protect your Edge and mail flow infrastructure from malicious relay from other Office 365 tenants. See the “Protect your Edge from malicious email relay by creating transport rules” section below.

Lets now start by creating the receive connector:

New-ReceiveConnector -Name “From EOP” -RemoteIPRanges [EOP remote Ranges] -Usage custom -AuthMechanism Tls -PermissionGroups AnonymousUsers, ExchangeUsers, ExchangeServers, Partners -Bindings

In the command above you should:

  • Change the name of the connector as per your naming convention
  • Add all EOP remote ranges that you can find here
  • Define specific bindings if relevant to your scenario

Once the connector is created, we need to add the necessary AD permissions:

Get-ReceiveConnector "From EOP" | Add-ADPermission -User 'NT AUTHORITY\Anonymous Logon' -ExtendedRights MS-Exch-SMTP-Accept-Any-Recipient

Before you add TLS to the connector, you need the certificate name:

$cert = Get-ExchangeCertificate -Thumbprint [Thumbprint of your third party Exchange certificate assigned to the SMTP service]
$tlscertificatename = "<i>$($cert.Issuer)<s>$($cert.Subject)"

Note: Make sure that you obtain, import and assign a proper third party certificate to your Exchange Server Edge, before you configure the receive connector

Now lets set all the necessary TLS properties in the connector:

Get-ReceiveConnector "From EOP" | Set-ReceiveConnector -AuthMechanism ExternalAuthoritative, Tls -RequireTls:$true -TlsDomainCapabilities -TlsCertificateName $tlscertificatename -fqdn

Make sure that the FQDN of the connector matches the certificate name.


Above a snapshot of the most relevant properties of the receive connector.

Send connector

Again here, for the send connector, you can have multiple scenarios. For mine, my Edge Server just has a simple send connector, not scoped to any transport rule or address space, that sends outbound email for all domains. Since this send connector is to send email to the Internet, no special TLS settings are configured.

The command I used was:

New-SendConnector -Internet -Name "To Internet" -AddressSpaces *

See here how to create a send connector and create your own.

Exchange Edge Address rewrite rules

Before I explain what rules you should create, I strongly encourage you to read this Address Rewriting on Microsoft Edge Servers article.

Address rewriting rules can be done per domain or per user. When you plan yours, think about if a domain type rule is enough or not, i.e do you want to translate to, or do you want to translate to, meaning is the prefix changing or not? If the answer is yes the prefix will change, then you need one rule per user.

Lets now check what we need to do for this specific scenario:

  • is on the first tenant and we need to translate his address to
  • is on the second tenant and we need to translate her address to

Now the rules:

New-AddressRewriteEntry -Name "John to" -InternalAddress -ExternalAddress
New-AddressRewriteEntry -Name "Mary to" -InternalAddress -ExternalAddress

The rules I created in this scenario are bi-directional.

Again you should consider per domain rules when applicable. You can also import the rules via CSV, to make sure you can create all rules with minimum effort.

Protect your Edge from malicious email relay by creating transport rules

When you set up the connectors for inbound and outbound relay of email, although the connections require TLS and only accept emails from Exchange Online, you have no control on what other Exchange Online tenants will try to do, and if they try to use your Edge to relay email.

To be able to control that, you need to create multiple transport rules in all of your Edge servers. Creating a transport rule in an Edge server is not as linear and doesn’t have the same available options that a CAS server has, but here’s what you need to do:

First exclude the recipient domains by creating one transport rule for each (or consolidating all in one) that basically stops transport rule processing if it finds a match:

New-TransportRule -Name "Emails from Outside to Inside" -FromScope NotInOrganization -AnyOfRecipientAddressContains "" -StopRuleProcessing $true
New-TransportRule -Name "Emails from Outside to Inside" -FromScope NotInOrganization -AnyOfRecipientAddressContains "" -StopRuleProcessing $true
New-TransportRule -Name "Emails from Outside to Inside" -FromScope NotInOrganization -AnyOfRecipientAddressContains "" -StopRuleProcessing $true

And finally the rule with less priority that will drop the email if it’s from outside of the Organization:

New-TransportRule -Name "Drop email if Outside of Organization" -FromScope NotInOrganization -DeleteMessage $true

The rules above must be in the right priority, meaning priority 0 to 2 should be to the rules that stop processing more rules, if an internal domain is detected.

Then, if there’s no internal domain detected in any of the recipients of the message, the Edge should drop the message. This is to allow that inbound email still works and outbound doesn’t allow malicious connections for relay.

Basically what you are saying in the last rule is that if the sender is not internal to your organization, have the Edge server just drop the email, if that rule ends up being processed. I strongly advise you to test the rule right after you implement it, and make sure it allows all the domains you need.

Lets show the rule in action:


As you can see above the email sent from a non accepted domain gets dropped by the Edge server. Also remember that the rules was processed because no other rule found a domain match in all recipients.

Consider the transport rules, although unfortunately very limited when applied in Edge Servers, when it comes to available actions and predicates, as an additional and important layer of security that you can apply to your infrastructure.

Accepted domains in the Edge Server

One of the important things I mentioned before, is that the Edge Server considers the outbound and inbound emails, sent to and from the domains you are translating to and from, as internal emails.

In my scenario all I had to begin with was a stand alone Edge Server, with no accepted domains, which might not be the case for you if you use an Edge in an existing Hybrid infrastructure, but if it is, here’s what you need to know about accepted domains:

  • Add the vanity domain from source and destination tenants, that you are translating from (outbound) and to (inbound)
  • Add the vanity domain both tenants are sharing


Here’s the snapshot of mine, just so you understand better what I needed for my scenario.

DNS Records

Now let me describe how my email related DNS records should be configured. Remember this is specific for my scenario. Also I am only covering the MX and SPF DNS records. Make sure that you apply the industry recommendations for email when configuring your email domains (i.e DKIM).

The MX records

Here’s how the MX records for all 3 domains are configured, in my scenario:

  • MX record points to EOP in the Office 365 tenant where the domain is valid
  • MX record points to EOP in the Office 365 tenant where the domain is valid
  • MX points to the pool of Edge Servers

The explanation for the above is simple, you need to make sure that any email outside of the address rewriting can go to the correct recipient. For example, if someone external emails, there’s no reason for the email to go via the address rewriting process or the Edge pool. And this applies to any external communication going to source and destination going directly to those domains.

As for the, the opposite happens, meaning if someone emails, the email must go to the Edge just so the inbound transport agent can translate that address to For that reason and because the Edge is the source of authority for the domain, that effectively is not an SMTP address in any recipient in my scenario, the MX for that domain needs to point there.

The SPF records

Here I opted for the safest approach and configured all SPF records the same way, for all 3 domains, to include senders from:

  • MX record
  • Exchange Online protection
  • The Edge server

Lets break this down:

  • and for this domains, hosted in Office 365, allowing the MX and EOP is redundant but it does no harm, and I allowed the Edge server for one simple reason, if the address translation fails for some reason, the email can still go from the Edge server outbound and the source address would be on of these domains, so for those unexpected scenarios you should add the Edge to the SPF.
  • for this domain, adding EOP is in fact not needed in my scenario, since there’s no point in time where the domain is expected to be moved to Office 365, but in most scenarios, specially migration scenarios, EOP should be in the allowed senders, so I added it. The Edge and the MX are again redundant, but you should have at least one of them.

Here’s how the SPF would look:

v=spf1 mx ip4:[Edge IPV4 Public address] ~all

How everything works

It’s time to test the scenario now. Here’s what I will do:

  • Send an outbound email from both tenants
  • Reply to the outbound emails
  • Send an inbound email to both tenants

The results we will analyze are:

  • Verify source and destination “from” and “to” addresses, looking at the email in the destination
  • Verify that TLS is being used
  • See the transport agents in action

I don’t want to prove my scenario with screenshots, since I don’t think that’s relevant and I’d have to grey out most of the information anyway, but below you can find some snippets of how it worked, just so we’re clear on what you should expect and also how to troubleshoot any issues.

Outbound email from both tenants

The email being sent at the source, from John:


And from Mary:


Now lets see what happens when the email hits the Edge server:



I used the message tracking log, and as you can see for the message I sent, the agent event “SETROUTE” is translating the address.

Lets look at the message in the destination:



… and now lets really look inside the message 🙂 (just one of them now)

The original mailfrom address:


The address translated:


And TLS being used:


Reply to the outbound emails

For the reply I will just use one user as the example, since the behavior is the same for both.


So lets see what happens in the Edge:


As you can see above, there’s two messages address to Mary, one is a reply and the other a brand new message, in both cases you can see that the recipient got translated. Unlike outbound email, you won’t see an event ID associated to the transport agent, but you can check in the recipients column that it did its job.

And finally, the external email in the internal mailbox:


That’s it. Enough screenshots. Hopefully you understood how everything works and how you can troubleshoot it.

The bottom line

Hopefully after reading this blog post, you can understand better how the mail flow coexistence can be done. This was in fact a simple scenario but more will come in future posts. If you want me to describe and blog about a specific scenario you have, or if you need help understanding it better, please drop me a line.

I’ve been working in the migration business for more than 5 years now, and I’ve seen a lot of partners and customers that do need mail flow coexistence between two tenants. Because it’s not simple for Microsoft to address that, and allow things like the same vanity domain in two tenants, some companies created products and services that try and fill that gap, but like I said previously in this post, handing over your mail flow pipeline is not a simple decision nor one that Enterprises are willing to take, specially when it’s not that hard for you to do it yourself and I am sure that building and maintaining a high available mail flow infrastructure (like Edge servers) is cheaper than paying a per user fee to get this functionality.

In fact, for many Enterprises, they already have what they need. This doesn’t necessarily has to be done with Edge servers. The top email appliances in the market can do this as well. I might include some of those scenarios in future posts.

Stay tuned and thank you for reading!













A mix of PowerShell and Graph API to create a Microsoft Teams test environment and test the BitTitan MigrationWiz Teams migration tool

For those of us that work a lot with cloud data migration projects, one of the challenges that at least I end up having is to create random data to migrate, that being to test a new migration workload or endpoint, to do a proof of concept or even to troubleshoot a specific issue.

This blog post is focused specifically in adding data to Microsoft Teams, so if for any reason, stated above or not, you need to populate your Microsoft Teams environment, keep reading.

And of course if you’re considering to migrate Microsoft Teams you should go to the BitTitan website and read more about it. We (have an awesome tool that you should definitely use to migrate Teams and if you reach out to me I can get you some help testing it, after you create your test environment with the help of this blog post!

What we will provide in this blog post is a script, authored by Ash Karczag and co-authored by me, that will leverage both PowerShell and the Graph API (yep that’s how awesome the script is), to create and populate a bunch of stuff in your Teams environment, in your Office 365 test tenant.

Note: This script wasn’t designed to be executed in production tenants, since all it creates is based on random names (i.e Team names, Channel names, etc) and it doesn’t have error handling or logging.

What will the script create?

The following actions will be executed by the script, to create objects in Office 365:

  • Create 2 users
  • Create 10 Teams
  • Create 5 team public channels, per Team
  • Publish 5 conversations in each channel of each Team
  • Upload 5 files to the SharePoint document library of each Team

Which SDK modules or API’s do you need to configure?

The script will leverage multiple SDK’s, for multiple different reasons that include read or create objects and the Microsoft Teams Graph API will be used to create the conversations and upload the files. So in summary, you need:

  • Microsoft Azure MSOL Module to connect to your Office 365 tenant (if you don’t have it installed, run “Install-Module MSOnline”)
  • Microsoft Teams PowerShell (if you don’t have it installed, run “Install-Module -name MicrosoftTeams”)
  • Microsoft Teams Graph API (instructions below on how to set it up in your tenant)

How to configure the Microsoft Teams Graph API authentication

The script requires Migration Teams Graph API access, which is done via OAuth2 authentication. The Graph API will be used to create conversations and to upload the files.

To configure the authentication, follow the steps below:

  1. Go to, sign in with global admin
  2. Select Azure Active Directory
  3. Select App Registrations
  4. Select + New Registration
  5. Enter a name for the application, for example “Microsoft Graph Native App”
  6. Select “accounts in this organizational directory only”
  7. Under Redirect URI, select the drop down and choose “Public client/native” and enter “;
  8. Select “Register”
  9. Make a note of your Application (client) ID, and your Directory (tenant) ID
  10. Under Manage, select “API Permissions”
  11. Click + Add Permission
  12. In the Request API Permissions blade, select “Microsoft Graph”
  13. Select “Delegated Permissions”
  14. Type “Group” in the Search
  15. Under the “Group” drop down, select “Group.ReadWrite.All”
  16. Select “Add Permissions”
  17. You will get a warning message that says “Permissions have changed, please wait a few minutes and then grant admin consent. Users and/or admins will have to consent even if they have already done so previously.”
  18. Click “Grant admin consent for <tenant>”
  19. Wait for permissions to finish propagating, you’ll see a green check-mark if it was successful
  20. Under Manage, select Certificates & Secrets
  21. Select “+ New client secret”
  22. Give the secret a name that indicates its purpose (ex. PowerShell automation secret)
  23. Under Expires, select Never
  25. Now you have the Client ID, Tenant ID, and Secret to authenticate to Graph using PowerShell

Once the authentication is configured and you have your secret key, you can proceed to executing the script.

How do I get the script

The script is published in Ash’s GitHub, and it’s called Populate_Teams_Data.ps1. Copy the content into a notepad or any script editor in your machine and save it in the same ps1 format.

How to execute the script

So now lets go over the steps to execute the script. I am going to number them, just so it’s easier for you to follow:

  • Open PowerShell – It is recommended that you open it as an Administrator, since the script will try and set the execution policy to RemoteSigned


  • Browse to the .ps1 file location and execute the following
.\Populate_Teams_Data.ps1 -AdminUser "<AdminUsername>" -AdminPass "<AdminPass>" -License "<LicenseSkuID>" -tenantId "<DirectoryID>" -clientId "<AppID>" -ClientSecret "<ClientSecret>"

The values above should be the following values:

    • Admin User – your Office 365 Global admin
    • Admin Pass – the password for the GA
    • License – the license AccountSkuId that you want to apply to the newly created users (Note: Connect to the MSOnline module and run the Get-MsolAccountSku cmdlet in case you don’t know what the value is)
    • TenantId – value that you obtained in step 9 of the section above (Directory)
    • ClientId – value that you obtained in step 9 of the section above (Application)
    • Secret – value that you obtained in step 24 of the section above

Script Output

The script will describe you the steps that is taking, during its execution, such as:

  • Creating the users and the teams


  • Adding users to Teams


  • Creating channels per team


  • Creating usable data in your teams


Additional notes about the script

The following should be considered when executing the script:

  • This script was designed and created to be ran against empty tenants. It’s ok if you have users or are using other workloads, but do not run this in a production tenant, since the script was not designed for that.
  • The script can be executed multiple times, although it was created for single execution. It will check if Teams and Channels need to be created, but it will try and create users always, unless the user already exists. Have that in mind if you choose to run the script multiple times, to create more usable data.
  • The script only creates small files in the Teams. If you want to do a migration test with a large volume of files, you’ll have to upload them manually.
  • The script leverages the Graph API, which is the optimal way to create messages and upload files into the Teams, but it’s also a Beta API, so sometimes you might see random timeouts.

We welcome all feedback you might have. Enjoy!




Apply folder permissions in a SharePoint Online document library using PowerShell

Being a consultant with a primarily messaging background, it’s always interesting for me to blog about SharePoint and be out of my comfort zone.

What I am going to show you today, is how do you apply permissions to folders, in a SharePoint online document library, using PowerShell.

So what PowerShell module should you use?

Let me start by saying that, there’s multiple ways to programatically apply those permissions, to a SharePoint library. In this case I am using the SharePoint PnP PowerShell Module.

In the link above, you will be able to learn a bit more about the SharePoint Patterns and Practices module, as well as follow the steps to install it. Be aware that the PnP commands use CSOM, so you might get throttled at some point, if you execute too many.

Now lets look at the code in detail

I will try and break down the script, just so you understand all it does and adapt to your needs properly.

Configuration hard-coded values

It’s always best that you don’t hard-code any values in your script, just so you don’t have to edit it each time you want to run it for a different scenario, but I wanted to keep this one simple, so here it goes:

#This value if for your SharePoint Online Team site URL. In my case my team name is “Team1”. Change yours accordingly
#This is your list name. If you run a Get-PnPList you’ll see that Documents is for the Shared Documents library. You will need this for the cmdlet that sets the permissions
#This is the user account you want to give permissions to
$UserAccount = “”
#Role that you want to add (see permissions section for more information)
$Role = “Contribute”
#Relative URL of the parent folder for all folders you are applying permissions to (see different folder section below for more information on how to change this to target another folder)
$FolderRelativeURL = “/sites/Test1/Shared Documents/”

Connect to the SharePoint PnP Online PowerShell

#Connect to PnP Online. You will get prompted for credentials.
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

Grab all Folders to apply permissions to

#I created a small try catch to exit the script if we can’t grab the folders
$AllFolders=Get-PnPFolderItem-FolderSiteRelativeUrl “/Shared Documents”-ItemType Folder -ErrorAction Stop
Write-Host”Failed to list the folders”-ForegroundColor Red


Apply the permissions

#And finally the code for the loop to go folder by folder and apply the permissions
Foreach ($Folder in $AllFolders){
$FolderItem=Get-PnPFolder-url $RelativeURL
Set-PnPListItemPermission-List $ListName-Identity $FolderItem.ListItemAllFields-User $UserAccount-AddRole $Role

Now the entire script for you to copy

#Config Variables

$SiteURL = ""


$FolderRelativeURL = "/sites/Test1/Shared Documents/"

$UserAccount = ""

$Role = "Contribute"

#Connect to PnP Online

Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)


$AllFolders=Get-PnPFolderItem-FolderSiteRelativeUrl "/Shared Documents"-ItemType Folder -ErrorAction Stop



Write-Host"Failed to list the folders"-ForegroundColor Red



Foreach ($Folder in $AllFolders){



$FolderItem=Get-PnPFolder-url $RelativeURL

Set-PnPListItemPermission-List $ListName-Identity $FolderItem.ListItemAllFields-User $UserAccount-AddRole $Role


What if I want to do a different folder

The code above is to apply permissions in the top level folders within the Shared Documents library of your Team site. If you want to change to a different folder, edit the following lines in the code:

  • Line 4 – $FolderRelativeUrl: Add the folder structure here (i.e “/sites/Test1/Shared Documents/FolderA/SubFolderB/”
  • Line 13 – FolderSiteRelativeURL parameter: Add the folder structure here as well (i.e “/Shared Documents/FolderA/SubFolderB”

How about permissions

The example above applies the role “Contributor” to the folders, for the user defined. If you want to know more details about which role to apply, please go to this excellent article to understand permission levels in SharePoint.

Final notes

I hope this post is helpful. Like I stated initially there’s easy ways to make the script more complex but easier to manage, such as removing hard-coded values or for example creating a loop to add permissions to multiple users. Using the code above as reference will for sure save you some time or give you that quick win you need.

Microsoft Teams PowerShell – A simple use case to get you started

Not long ago I blogged about the new Microsoft Teams PowerShell module. Today I want to give you a quick example of how you can leverage it, to automate and make your work more efficient. I’ll show you how to list all Team Channels in your organization.

Connect to your Microsoft Teams Organization using PowerShell

The first thing you need to do is connect your Teams PowerShell module and authenticate to your Office 365 tenant.

  • If you don’t have the Microsoft Teams PowerShell module installed, click on the link in this article and install it
  • Once you have it installed the Connect-MicrosoftTeams cmdlet should be available. It’s as easy as running it and use the authentication prompt to pass the credentials, but you can also pass basic credentials if you want to, using the -credential parameter


List all Teams in your Microsoft Teams organization

To list all Teams in your organization, you can use the Get-Team cmdlet. By default the cmdlet will have as output the GroupID, DisplayName, Visibility, Archived, MailNickName and Description.


You can format your output to include any relevant Team attribute. Do a “Get-Team |fl” to list them all.

List all Team Channels in your organization

Now finally lets execute the use case of this post. To list all Team Channels in your organization, you can leverage the Get-TeamChannel cmdlet.

This cmdlet has a mandatory parameter -GroupID, which is basically the ID of each Team. That said you have two options:

Option 1: you run “Get-TeamChannel -groupid <TeamGroupID>”


You can use the Get-Team cmdlet to get the GroupId value for each team.

Option 2: you grab all Teams into an array and process each Team to list their channels, using the code snippet below.

$AllTeams = Get-Team

Foreach ($team in $AllTeams) {Get-TeamChannel -groupid $team.groupid |Ft $team.DisplayName, DisplayName}


What I did above, was changing the output of the command, to list in a readable way to which Team the Channels belong to. There are other ways, more organized, to format the output both to the console or an output file. Nevertheless this can easily guide you in that direction, and if you need any help let me know.

And that’s it. I can and will blog much more around Teams PowerShell. If you haven’t used it yet, you should.

Happy coding!


Use a Multi Factor Authentication enabled account to connect to Exchange Online PowerShell

Security is a big theme for any online applications and Multi Factor Authentication is used more and more everyday. Those security standards easily extend to PowerShell or any other admin sessions, to execute tasks in your tenant. It will be more and more common to see organizations that no longer allow their IT administrators to connect to their Exchange Online using basic auth.

If you’re a heavy PowerShell user like me, this is for you. Microsoft has an excellent article on how to leverage an MFA account to connect to Exchange Online PowerShell.

You should read the article, as this things tend to be updated and you’ll be sure the have the latest steps, but in essence what you need to do is:

  • Make sure you have all prerequisites (i.e .Net Framework), specially in older versions of Windows
  • Install the Exchange Online Remote PowerShell module (from the Exchange Online EAC)
  • Make sure that WinRM allows basic authentication

Finally you can run the following command to connect to Office 365 commercial:

Connect-EXOPSSession -UserPrincipalName

And if you are connecting to a different instance, such as GCC High or 365 Germany, you need to specify the ConnectionUri and the AzureADAuthorizationEndPointUri parameters (see official article for parameter configurations).

Connect-EXOPSSession -UserPrincipalName <UPN> [-ConnectionUri <ConnectionUri> -AzureADAuthorizationEndPointUri <AzureADUri>]

Here’s how the PowerShell session looks like after you install the module.


And the authentication process.


And that’s it. Happy scripting!!!


Goodbye 16-character limit for Azure AD passwords

Finally the change many were waiting for. The password length limit in Azure AD just went up from 16 to 256 characters and is now more in line with the On Premises AD limits.

Is there any immediate impact for the end user?

Not really. Apart from the fact that they will now be allowed to set longer passwords (and you should make them aware of that), the impact from the end user perspective should be null.

What about IT administrators and any external app that leverages Azure AD for integration?

The only thing you’ll probably need to be aware, before you start changing those service account passwords to 200+ character passwords (in the name of maximum security), is can your printer or your 3rd party app, that interacts directly with Azure AD or Office 365, support such long passwords? Be careful with that and you should be fine! 🙂


The Microsoft Teams PowerShell module GA is here

Great news for those who, like me, are heavy users of PowerShell and its available modules for the different Microsoft workloads: The Microsoft Teams PowerShell module is now in general availability, after being in Beta since last year.

There are several new functionalities, but the one I want to highlight is the new switch -TeamsEnvironmentName in the Connect-MicrosoftTeams cmdlet, that allows you to connect to environments different from 365 commercial, such as Government tenants.

If you haven’t already, read the Teams PowerShell Overview Microsoft documents.

I will be posting about Teams PowerShell scripting soon. Enjoy!

How do you plan and execute a successful Public Folder migration?

From all the years as a consultant, and now directly in the migration business, helping partners successfully plan and execute migrations, Public Folders as always been the one of the most challenging workloads I had to deal with.

There’s always a lot of questions when you have to execute such migrations, so I decided to write a blog post about it, where I am going to try and address as much as possible.

To try and keep it as organized as possible, and because there are so many different scenarios, I will divide this post into three main sections: General migration considerations, Migrating Hybrid Public Folders and Migrating Public Folders cross organization.

We will then also discuss some more generic questions, such as why use a third party tool vs the Microsoft native tool.

General migration considerations

This blog post is focused both on Hybrid and cross organization Public Folder migrations. Some steps however are exactly the same, regardless of what the migration scenario is. Those steps are described below. After reading this section you can then focus on your specific scenario in the sections that follow.

Prepare your On Premises Environment

One of the first things you need to look at is to the On Premises Public folder structure, to check if there’s any inconsistencies or invalid folders. The best way to that is of course via scripting, and you should use this excellent script from Aaron @Microsoft, called IDFix for Public Folders. Download it, run it and fix everything that the script highlights as needing to be fixed.

You should also make sure you create a report with all mail enabled Public Folders and address, and to do so you can leverage the Get-MailPublicFolder cmdlet.

How to migrate Public Folder access permissions, as well as Send-As and Send-on-behalf rights

Public Folder permissions should be migrated by the migration tool, provided of course identities match between on premises and Exchange Online (which should of course be true for Hybrid scenarios), or between premises  in cross organization migrations.

As for the Send-As and Send-on-behalf rights, the best option is to export them from the source system and import them into the destination system, once the migration is completed. Since this is not PowerShell code I’ve focused on recently, I did a quick research online and found this article online where you can find the code to export and import those access rights.

Note: I am not the author of the code below and I am only putting it directly in my blog post just so it’s easier for you to locate it and copy it. The code was taken from the article mentioned in the line above, written by Aaron Guilmette.

Export Send-As

Get-MailPublicFolder -ResultSize Unlimited | Get-ADPermission | ? {($_.ExtendedRights -Like "Send-As") -and ($_.IsInherited -eq $False) -and -not ($_.User -like "*S-1-5-21-*")} | Select Identity,User | Export-Csv Send_As.csv -NoTypeInformation

Export Send-on-behalf

Get-MailPublicFolder | Select Alias,PrimarySmtpAddress,@{N="GrantSendOnBehalfTo";E={$_.GrantSendOnBehalfTo -join "|"}} | Export-Csv GrantSendOnBehalfTo.csv -NoTypeInformation

$File = Import-Csv .\GrantSendOnBehalfTo.csv
$Data = @()
Foreach ($line in $File)
    If ($line.GrantSendOnBehalfTo)
        Write-Host -ForegroundColor Green "Processing Public Folder $($line.Alias)"
        [array]$LineRecipients = $line.GrantSendOnBehalfTo.Split("|")
        Foreach ($Recipient in $LineRecipients)
            Write-Host -ForegroundColor DarkGreen "     $($Recipient)"
            $GrantSendOnBehalfTo = (Get-Recipient $Recipient).PrimarySmtpAddress
            $LineData = New-Object PSCustomObject
            $LineData | Add-Member -Type NoteProperty -Name Alias -Value $line.Alias
            $LineData | Add-Member -Type NoteProperty -Name PrimarySmtpAddress -Value $line.PrimarySmtpAddress
            $LineData | Add-Member -Type NoteProperty -Name GrantSendOnBehalfTo -Value $GrantSendOnBehalfTo
            $Data += $LineData
$Data | Export-Csv .\GrantSendOnBehalfTo-Resolved.csv -NoTypeInformation

Import Send-As

$SendAs = Import-Csv .\SendAs.csv
foreach ($obj in $SendAs) 
    write-host "$($i)/$($SendAs.Count) adding $($obj.User) to $($obj.Identity)"
    Add-RecipientPermission -Identity $obj.Identity.Split("/")[2] -Trustee $obj.User.Split("\")[1] -AccessRights SendAs -confirm:$false; $i++

Import Send-on-behalf

$GrantSendOnBehalfTo = Import-Csv .\GrantSendOnBehalfTo-Resolved.csv
Foreach ($obj in $GrantSendOnBehalfTo)
    Write-host "$($i)/$($grantsendonbehalfto.count) Granting $($obj.GrantSendOnBehalfTo) Send-On-Behalf to folder $($obj.PrimarySmtpAddress)"
    Set-MailPublicFolder -Identity $obj.PrimarySmtpAddress -GrantSendOnBehalfTo $obj.GrantSendOnBehalfTo

Migrating Hybrid Public Folders

This scenario, when compared to the cross organization migration, is far more complex, because besides moving the data you will also have to worry about things like mail flow, user public folder access, etc. But lets address one thing at the time.

Microsoft Official guidance to configure Hybrid Public Folders

If you’re reading this article because you’re planning to migrate your Hybrid Public folders, chances are you already read and executed the Microsoft guidance to make your on premises Public Folders available to Exchange Online users, under a Hybrid deployment. Configure legacy on-premises Public Folders for a Hybrid Deployment is the article for legacy public folders and Configure Exchange Server Public Folders for a Hybrid Deployment is the one for modern Public Folders.

Both articles are focused on the hybrid coexistence and not the migration planning of the Public Folders, but they are important to mention as they impact the migration planning, based on what type of coexistence you configured and steps you followed.

Public Folder end user access in the context of a hybrid migration

When planning a Public Folder migration, under a hybrid scenario, one of the most important things you need to consider is, end user access. With that in mind, consider the following:

  • On premises users cannot access Exchange Online Public Folders
  • Exchange Online users can access on premises public folders and/or Exchange Online Public folders, although you cannot configure a single user to access both, you can configure some users to have access to on premises folders and some to see them locally, in Exchange Online.

Have the two principles in mind, during your planning. The Public Folder access for Exchange Online users is complex and by itself worthy of a dedicated blog post.

The Microsoft official guidance, mentioned in the previous section, explains how you configure Exchange Online users to access on premises Public Folders.

The bottom line of this section is, make sure you move all users to Exchange Online, before you consider moving the Public Folders, and if you don’t, make sure that the users left on premises do not require any Public Folder access.

Public Folder mail flow coexistence before, during and after the migration. How do you handle mail enabled Public Folders.

Another very important component of your Public Folder migration is the mail flow coexistence, or to be more precise, the way you deal with the mail enabled Public Folders.

Mail Enabled Public Folders before the migration

When you follow the guidance provided by Microsoft, you will be asked to execute the Sync-MailPublicFolders script.

This script enables Exchange Online users to send emails to on premises mail enabled Public Folders, by creating mail objects in Exchange Online with the primary and all other SMTP addresses that those folders have on premises. This objects are not actual Exchange Online Public Folder nor they are visible in the Exchange Online Public Folder tree. They also allow those on premises Public Folders to be present in the Exchange Online GAL (Global Address List), and once a user in Exchange Online emails that folder, the email gets forwarded to Exchange On Premises.

Mail Enabled Public Folders during the migration

During the Public Folder migration, whether it’s a single or multiple pass (with pre-stage + full migration) migration strategy, you should not change the Public Folder mail flow. That means that you should not mail enable the Public Folders in Exchange Online (chose a tool that gives you that option). Actually as you will see below, there are things that you need to do in Exchange Online, before mail enabling the Public Folders.

Mail Enabled Public Folders after the migration

Once your migration (or the pre-stage) is completed, you should transition the Public Folder mail flow to Exchange Online. To do so, you should follow these steps:

  1. Start the pre-stage or full migration and wait for it to be completed
  2. Once the migration pass is done, go to Exchange Online and delete all mail objects created by the Sync-MailPublicFolders script (NOTE: this will temporarily break mail flow between Exchange Online users and mail enabled Public Folders, online or on premises)
  3. Mail enable the Exchange Online Public Folders, either via a script or using the migration tool. Make sure you add all addresses from the on premises to the online Public Folders
  4. Run a full migration pass if in step 1 the pass that you ran was a pre-stage

To elaborate a little bit more in step 2, the reason that you need to delete those objects is because you need to avoid conflicting addresses, when enabling the mail enabled Public Folders in Exchange Online, and those objects are not associated with the new EXO Public Folders.

Migrating Public Folders cross Organization

Migrating Public Folders cross organization is not as complex, and you’ll see why in the sections below. This scenario can include migrations such as:

  • Exchange Online to Exchange Online
  • Hosted Exchange to Exchange on premises or Exchange Online
  • Exchange on premises to Exchange on premises

When to migrate users and Public Folders

Usually this Public Folder migrations cross organization come as an additional step to a migration that also includes mailboxes.

Although there’s no 100% correct answer, when it comes to the question of what should be migrated first, mailboxes or Public Folders, in this cases normally the best option is to migrate mailboxes first and Public Folders last. The main reason for that is because you should migrate the Public Folders when they’re not being used anymore, allowing you to do a clean single pass migration of all the data.

Public Folder end user access and mail flow coexistence

This is where things gets simple, for this type of scenarios. There’s no Public Folder access cross organization (unless the user is using the credentials for the 2 systems) and although technically you can configure mail flow between any two email systems, it’s not something you should consider for the majority of the cases.

Mail enabled Public Folders can and should be created at the destination during the folder hierarchy creation.

Why use a third party tool to migrate Public Folders

That’s probably the question I get the most, working for a third party migration tool company, that has an amazing Public Folder migration tool, BitTitan. And here is a list of reasons:

  • Migrate large volumes of data: Migrating 2, 5 or 10GB is easy with any tool, but not all tools can deal with Terabytes of Public Folder data.
  • Migrate parts of the structure or prioritize data: Either by targeting just specific parts of the Public Folder hierarchy or by using folder filtering. This is a very commonly used feature in tools like BitTitan MigrationWiz.
  • Flexibility on handling mail enabled Public Folders: As explained in the Hybrid mail flow section of this posts, you might need some flexibility on how to handle mail enabled Public Folders during the migration. MigrationWiz will mail enable in the destination all Public Folders that are mail enabled at the source, but you can also suppress that option, and should in some scenarios.
  • Data transformation: While planning a migration of Public Folders, some customers want to take that opportunity to also move that data into a different structure, which can be shared or resource mailboxes, office 365 groups, etc. That is something that can be successfully done with tools that are flexible enough to perform that transformation (i.e in many cases requires recipient mapping, folder mapping, folder filtering, etc), like MigrationWiz.
  • Supported sources and destinations: Exchange 2007+ to Exchange 2007+, including of course Exchange Online and hosted as source and/or destination – this is the answer most customers want to hear from the support ability stand point of a third party tool, to migrate their Public Folders, and that is something they won’t get with the native tool.

The bottom line

While reading this post, before publishing it, I always get the feeling that there’s so many other things that I could mention and talk about, but I do think that it addresses the core concerns of most Public Folder migrations, and hopefully it addresses yours.

Nevertheless, if you do have any questions don’t hesitate to reach out.




Office 365: Not sure if your vanity domain is being used by another Office 365 tenant? Don’t worry.

I remember not long ago, the pain of trying to find out in which Office 365 tenant your vanity domain was validated, when you bumped into the error stating that the domain was in use, while adding it to your current tenant.

This was maybe because I work with multiple tenants and I recycle my tenants quite often, for testing purposes, but I’ve also seen it with others while trying to assist them in their migration projects.

Fortunately Microsoft now is very clear in the error message you’ll see, when trying to add a domain to a tenant, that is in use. This is what you will see now:


As you can see above, it will tell you exactly in which tenant the domain is, just so you can login to it and remove it.

Now is there a catch with this? Of course!! Microsoft won’t give you such privileged information, until you enter a valid DNS record for the domain validation. You’ll see something similar to this, if the domain validation is not done properly:


So remember to add the DNS record first and click “Verify”, Microsoft will either add the domain or explain exactly why they can’t, which I am sure it was for a long time one of the main asks of Office 365 admins and consultants.