Automating with PowerShell: Checking if you can move to M365 BP from O365 E3

So with M365 BP having nearly all the features that E3 gives you, albeit with some limitations a lot of our clients are moving their E3 licenses over. M365BP has the added benefit of Intune/Autopilot, P1 licenses, etc. So all pretty awesome stuff.

Before you can move a client over, you’ll have to check some small things. We have a fairly lengthy internal script to perform all these checks, but I figured to share a part of this script for the public at large. 😉

The Script

This script is a simplified version of our internal one; It checks the following things for you;

  • If you are currently using more than 300 licenses, because if you are you will have to Mix-and-Match all the overage of the licenses.
  • If there are mailboxes larger than 50GB. If so, these have to be licensed differently or cleaned up first.
  • If your clients connecting are all recent clients. Automatic downgrades of the licenses without reinstallation requires a recent version of the installed O365 application.

There’s some more caveats, but these are the most major ones you can tackle with this script. Licenses are tricky business so customize this script to your wishes if you want to add/remove anything. 🙂

As with most of my scripts you’ll need the secure application model. The resulting files will be written to C:\Temp.

######### Secrets #########
$ApplicationId = 'YourApplicationID'
$ApplicationSecret = 'YourAppicationSecret' | ConvertTo-SecureString -Force -AsPlainText
$TenantID = 'YourTenantID'
$RefreshToken = 'insanelylongtoken'
######### Secrets #########
install-module PSWriteHTML
$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)

$aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes '' -ServicePrincipal -Tenant $tenantID 
$graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes '' -ServicePrincipal -Tenant $tenantID 

Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken

$customers = Get-MsolPartnerContract -All

foreach ($customer in $customers) {

    $UserLicenses = Get-MsolAccountSku -TenantId $customer.TenantId
    if ($UserLicenses.AccountSkuId -like "*ENTERPRISEPACK*") {
        write-host "$($Customer.DefaultDomainName) has ENTERPRISEPACK"
    else {
        write-host "Skipping $($Customer.DefaultDomainName) as they do not have an ENTERPRISEPACK" -ForegroundColor Yellow 
    $LicObject = foreach ($UserLicense in $UserLicenses) {
        if ($UserLicense.ConsumedUnits -gt '300') {
                Name          = ($UserLicense.AccountSkuID -split ':')
                ConsumedUnits = $UserLicense.ConsumedUnits
                Warning       = "Microsoft 365 BP can only be used for the first 300 users. If this license is contained in a package. Please note to buy a seperate license for the users with overage."
    if (!$LicObject) {
        $LicObject = [PSCustomObject]@{
            Report = 'No licensing conflicts found. License migration can be performed without issues.'

    write-host "Generating token for $($" -ForegroundColor Green
    $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes '' -ServicePrincipal -Tenant $customer.TenantID
    $Header = @{
        Authorization = "Bearer $($graphToken.AccessToken)"
    $MailboxUsage = (Invoke-RestMethod -Uri  "'D7')" -Headers $Header -Method Get -ContentType "application/json") -replace "", "" | ConvertFrom-Csv
    $LargeMailboxes = foreach ($Mailbox in $MailboxUsage) {
        if ([int64]$Mailbox.'Storage Used (Byte)' -gt 45GB) { 
                Username      = $Mailbox.'User Principal Name'
                DisplayName   = $Mailbox.'DisplayName'
                StorageUsedGB = [math]::round($Mailbox.'Storage Used (Byte)' / 1gb, 2)
    if (!$LargeMailboxes) {
        $LargeMailboxes = [PSCustomObject]@{
            Report = 'No mailbox sizing issues found. Technical mailbox migration can be performed without issues.'

    $VersionReport = (Invoke-RestMethod -Uri "'D7')" -Headers $Header -Method get -ContentType "application/json") -replace "", "" | ConvertFrom-Csv
    $LegacyClients = if ($versionreport.'Outlook 2007' -or $versionreport.'Outlook 2010' -or $versionreport.'Outlook 2013' -or $versionreport.'Outlook 2016') {
    if (!$LegacyClients) {
        $LegacyClients = [PSCustomObject]@{
            Report = 'No Legacy outlook clients found. License change can be performed without issues.'

    start-sleep -Milliseconds 500  #sleep to prevent CSV Throttle on Graph API

    New-HTML {
        New-HTMLTab -Name "O365 to M365 Preperation Report" {
            New-HTMLSection -Invisible {
                New-HTMLSection -HeaderText 'License Settings' {
                    New-HTMLTable -DataTable $LicObject
            New-HTMLSection -Invisible {
                New-HTMLSection -HeaderText "Mailbox Settings" {
                    New-HTMLTable -DataTable $LargeMailboxes
                New-HTMLSection -HeaderText "O365 Client Usage" { 
                    New-HTMLTable -DataTable $LegacyClients
    } -FilePath "C:\temp\$($customer.DefaultDomainName).html" -Online

I hope this helps you migrate your clients over, as always, Happy PowerShelling!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.