Find and remove unnecessary licenses on shared mailboxes in Office 365 customer tenants

Remove Unnecessary Licenses from Office 365 Shared Mailboxes

It’s common for MSPs to convert Office 365 users to Shared Mailboxes when offboarding them. It’s essentially a free mailbox that you can redirect, attach to someone else, or convert to a regular mailbox if the user returns.

Unfortunately, if you’re manually offboarding users and you forget to remove the user’s Office 365 license, you’ll continue to be billed for it. One way to prevent this is to use an offboarding script (I’ll upload a version of ours in the next week or so). However it’s also a good idea to do an audit of your customers’ users and their licenses every now and then, just in case some have fallen through the cracks.

The script below will check all of your customers’ Office 365 tenants for licensed shared mailboxes.

If some are found, it’ll export a csv of them. If you’re happy to remove the licenses from all of them at once, you can do so. Otherwise you can be asked to decide for each one.

Want to run this on a single Office 365 tenant?

This script is for Microsoft Partners. If you’d like to run this on a single Office 365 tenant, see here for a separate version of it.

Removed a license from a shared mailbox by mistake?

The script will also export a CSV with a summary of all licenses assigned to shared mailboxes, including a PowerShell cmdlet that you can use to reassign licenses. If you use this script and remove a license in error, just copy and paste the relevant cmdlet into a PowerShell session that’s connected to Office 365 as a delegated admin.Commands To Re-add Licenses Removed In Error

Some things to keep in mind

  • This script does not support 2 factor authentication on the delegated admin account.
  • This script is for Microsoft Partners managing multiple tenants via delegated administration. See here for a version of this script that works across your own Office 365 tenant.

Keep track of unused Office 365 licenses

Once you’ve run this script, you may want to run this one as well. It’ll give you a report of all unused Office 365 licenses in all customer tenants.

You can also follow our guide here to set up an Azure Function and Microsoft Flow to receive email alerts about unused licenses.

How to run the script to find licensed shared mailboxes

  1. To run the script, copy and paste the code below into Visual Studio Code.
  2. Save it as a PowerShell (.ps1) file. Make sure you’ve installed the PowerShell extension in Visual Studio Code. You should be prompted for this when you save it if it’s not installed already.
  3. Press F5 to run it.
  4. Enter the credentials of an Office 365 admin with delegated access to customer tenants.
  5. Wait for it to complete. This one takes a while because it needs to connect to Exchange Online for each of your tenants and retrieve their mailbox details.Checking Licensed Shared Mailboxes In Office 365 Tenants
  6. When it completes, a couple of CSVs will be exported to c:\temp. One will contain a list of all licensed shared mailboxes and their details. The other will contain a list of each license attached to the shared mailbox, and a command that you can use to re-add it (see image above).List Of Licensed Shared Mailboxes In Office 365
  7. You can choose to remove all shared mailboxes at once (not recommended until you’ve checked the exported CSV), or get prompted for each mailbox.Found Shared Mailboxes In Customer Office 365 TenantsRemoving Licenses From Shared Mailboxes

Complete script for reporting on licensed Shared Mailboxes in all Office 365 customer tenants

#Establish a PowerShell session with Office 365. You'll be prompted for your Delegated Admin credentials
$Cred = Get-Credential
Connect-MsolService -Credential $Cred
$customers = Get-MsolPartnerContract
Write-Host "Found $($customers.Count) customers for $((Get-MsolCompanyInformation).displayname)." -ForegroundColor DarkGreen
$CSVpath = "C:\Temp\LicensedSharedMailboxes.csv"
$LicenseReportpath = "C:\Temp\SharedMailboxLicenseReport.csv"
$licensedSharedMailboxes = @()

foreach ($customer in $customers) {
    $licensedUsers = Get-MsolUser -TenantId $customer.TenantId | Where-Object {$_.islicensed}

    $ScriptBlock = {Get-Mailbox -ResultSize Unlimited}
    $InitialDomain = Get-MsolDomain -TenantId $customer.TenantId | Where-Object {$_.IsInitial -eq $true}    
    Write-Host "Checking Shared Mailboxes for $($Customer.Name)" -ForegroundColor Green
    $DelegatedOrgURL = "https://outlook.office365.com/powershell-liveid?DelegatedOrg=" + $InitialDomain.Name
    $sharedMailboxes = Invoke-Command -ConnectionUri $DelegatedOrgURL -Credential $Cred -Authentication Basic -ConfigurationName Microsoft.Exchange -AllowRedirection -ScriptBlock $ScriptBlock -HideComputerName -ErrorAction SilentlyContinue
    $sharedMailboxes = $sharedMailboxes | Where-Object {$_.RecipientTypeDetails -contains "SharedMailbox"}

    foreach ($mailbox in $sharedMailboxes) {
        $licensedSharedMailboxProperties = $null
        if ($licensedUsers.ObjectId -contains $mailbox.ExternalDirectoryObjectID) {
            Write-Host "$($mailbox.displayname) is a licensed shared mailbox" -ForegroundColor Yellow  
            $licenses = ($licensedUsers | Where-Object {$_.objectid -contains $mailbox.ExternalDirectoryObjectId}).Licenses
            $licenseArray = $licenses | foreach-Object {$_.AccountSkuId}
            $licenseString = $licenseArray -join ","
            Write-Host "$($mailbox.displayname) has $licenseString" -ForegroundColor Blue
            $licensedSharedMailboxProperties = @{
                CustomerName      = $customer.Name
                DisplayName       = $mailbox.DisplayName
                EmailAddress      = $mailbox.PrimarySmtpAddress
                Licenses          = $licenseString
                TenantId          = $customer.TenantId
                UserPrincipalName = ($licensedusers | Where-Object {$_.objectid -contains $mailbox.ExternalDirectoryObjectID}).UserPrincipalName
            }
            $forcsv = New-Object psobject -Property $licensedSharedMailboxProperties
            $licensedSharedMailboxes += $forcsv
            $forcsv | Select-Object CustomerName, DisplayName, EmailAddress, Licenses | Export-CSV -Path $CSVpath -Append -NoTypeInformation
            # Create a CSV with a license report and PowerShell Cmdlets that you can use to quickly reassign licenses if you've removed them in error. 
            foreach ($license in $licenses) {
                $licenseProperties = @{
                    CustomerName = $customer.Name
                    DisplayName  = $licensedSharedMailboxProperties.DisplayName
                    EmailAddress = $licensedSharedMailboxProperties.UserPrincipalName
                    License      = $license.AccountSkuId
                    TenantId     = $customer.TenantId
                    ReAddlicense = "Set-MsolUserLicense -UserPrincipalName $($licensedSharedMailboxProperties.UserPrincipalName) -TenantId $($customer.TenantId) -AddLicenses $($license.AccountSkuId)"
                }
                $forcsv = New-Object psobject -Property $licenseProperties
                $forcsv | Export-CSV -Path $LicenseReportpath -Append -NoTypeInformation
            }
        }
        else {   
            Write-Host "$($mailbox.DisplayName) is unlicensed"
        }
    }
}
# Provide an option to remove the licenses from the shared mailboxes

Write-Host "`nFound $($licensedSharedMailboxes.Count) licensed shared mailboxes in your customers' tenants. A list has been exported to $csvpath
A license report has been exported to $licenseReportPath, just in case you need to restore these licenses to the shared mailboxes later." -ForegroundColor Yellow
Write-Host "r: Press 'r' to remove all licenses from all shared mailboxes."
Write-Host "a: Press 'a' to be asked for each mailbox."
Write-Host "l: Press 'q' to quit and leave all licensed."

do {
    $input = Read-Host "Please make a selection"
    switch ($input) {
        "r" {
            Clear-Host
            Write-Host "Removing licenses from the following sharedmailboxes: `n$($licensedSharedMailboxes.userprincipalname -join ", ")"
            foreach ($mailbox in $licensedSharedMailboxes) {
                $currentLicenses = $null
                $licenses = $mailbox.Licenses -split ","
                foreach ($license in $licenses) {
                    Write-Host "Removing $license" -ForegroundColor Yellow
                    Set-MsolUserLicense -UserPrincipalName $($mailbox.UserPrincipalName) -TenantId $($mailbox.TenantId) -removelicenses $License
                }
                $currentLicenses = (Get-MsolUser -UserPrincipalName $($mailbox.UserPrincipalName) -TenantId $($mailbox.TenantId)).Licenses
                if (!$currentLicenses) {
                    Write-Host "License successfully removed from $($Mailbox.displayname) ($($mailbox.UserPrincipalName)): $($mailbox.Licenses)" -ForegroundColor Green
                }
                else {
                    Write-Host "License was not successfully removed from $($Mailbox.displayname) ($($mailbox.UserPrincipalName)), please remove licenses via the Office 365 Portal: $($mailbox.Licenses)" -ForegroundColor Red
                }
            }
            Read-Host "Enter q to quit."
            $input = "q"
            return
        } "a" {
            Clear-Host
            foreach ($mailbox in $licensedSharedMailboxes) {
                $mailboxChoice = $null
                do {
                    $mailboxChoice = Read-Host "Would you like to remove $($mailbox.licenses) from $($Mailbox.displayname) ($($mailbox.UserPrincipalName))? [y,n]"
                    switch ($mailboxChoice) {
                        "y" {
                            $currentLicenses = $null
                            $licenses = $mailbox.Licenses -split ","
                            foreach ($license in $licenses) {
                                Write-Host "Removing $license" -ForegroundColor Yellow
                                Set-MsolUserLicense -UserPrincipalName $($mailbox.UserPrincipalName) -TenantId $($mailbox.TenantId) -removelicenses $License
                            }
                            $currentLicenses = (Get-MsolUser -UserPrincipalName $($mailbox.UserPrincipalName) -TenantId $($mailbox.TenantId)).Licenses
                            if (!$currentLicenses) {
                                Write-Host "License successfully removed from $($Mailbox.displayname) ($($mailbox.UserPrincipalName)): $($mailbox.Licenses)" -ForegroundColor Green
                            }
                            else {
                                Write-Host "License was not successfully removed from $($Mailbox.displayname) ($($mailbox.UserPrincipalName)), please remove licenses via the Office 365 Portal: $($mailbox.Licenses)" -ForegroundColor Red
                            }
                        }
                        "n" {
                            Write-Host "Leaving $($mailbox.licenses) on $($mailbox.EmailAddress)" 
                        }  
                    } Pause
                }until($mailboxchoice -eq "y" -or $mailboxChoice -eq "n")  
            }
            return
        } "q" {
            return
        }
    }
    pause
}
until ($input -eq "q")

Was this article helpful?

Related Articles