Unlicensed Mailboxes on Exchange Online


Hello All,

First off, I am not a usual blogger or an active technical forums participant 🙂 . But, I've got help from many blogs and forums many a times and I thought it would be mindful to repay by sharing the findings and scripts I discover from my field engagements.

Btw, my name is Santhosh, work as a Sr PFE with Microsoft and been with Exchange Team for almost 8 years now. This is my first entry on my personal blog, although am more active on Exchange on-perm side of things, i thought it would be splendid to start it off with an Exchange Online post.


One of my customer has Hybrid environment and often does on-boarding of mailboxes to Exchange Online. Out of the Odds, one mailbox was not licensed and was removed from Exchange Online. It was too late when the user reported the loss of mailbox to helpdesk, and when it was brought to our notice, the Mailbox was permanently deleted from Exchange Online. The user got a brand new mailbox. As of now, Exchange Online permits usage of Unlicensed mailboxes for 30 days from the Mailbox creation date. You may find mailboxes that are not licensed and accessible even after 30 days, the mailbox stay longer sometimes as the workflow deletes the mailboxes no sooner than 30 days Old. It is recommended to assign licenses within the 30 day grace period.

You could retrieve the list of unlicensed mailboxes from Portal or using powershell.

Below is the sample powershell code I provided for them to use to schedule a report generation monthly and work on it. Unfortunately, the blog doesn't let me post the code with the same syntax and color as I see in Powershell ISE. As this is a sample and a small script, I will just drop it here.


#################################################################################
# 
# The sample scripts are not supported under any Microsoft standard support 
# program or service. The sample scripts are provided AS IS without warranty 
# of any kind. Microsoft further disclaims all implied warranties including, without 
# limitation, any implied warranties of merchantability or of fitness for a particular 
# purpose. The entire risk arising out of the use or performance of the sample scripts 
# and documentation remains with you. In no event shall Microsoft, its authors, or 
# anyone else involved in the creation, production, or delivery of the scripts be liable 
# for any damages whatsoever (including, without limitation, damages for loss of business 
# profits, business interruption, loss of business information, or other pecuniary loss) 
# arising out of the use of or inability to use the sample scripts or documentation, 
# even if Microsoft has been advised of the possibility of such damages
#
#################################################################################
#
# Author: Santhosh Sethumadhavan <santhse@microsoft.com>
#
#################################################################################

#Requires -Modules Msonline
Set-StrictMode -Version Latest

#Check if MSOLService is connected

try
{
 Get-MsolDomain -ErrorAction Stop | Out-Null
}
catch 
{
 Write-Error "The MSOLService is not connected, please run Connect-MSOLService and try again"
 Exit
}


#Find the Remote session that is currently active. If for some reasons the session broke inbetween the script, user needs to fix it and re-run the script
$RemoteSession = Get-PSSession | Where-Object{$_.state -eq 'opened' -and $_.configurationname -eq 'Microsoft.Exchange' -and $_.ComputerName -eq 'outlook.office365.com' } | Select-Object -First 1
If(-not $RemoteSession){
 Write-Error "Unable to find the session configuration, please reconnect to Exchange online powershell session and try again. "
 Exit
}

#Check if the Outputfile exists, if yes just rename and continue
[STRING]$Outfile = "C:\Temp\UnlicensedMbxs_$(Get-Date -format "yyyyMMdd").CSV"
if(Test-Path $Outfile){
 Rename-Item $Outfile "$Outfile.old"

}

#LicenseReconciliationNeededOnly - indicates whether an additional license assignment is required to bring the user into compliance
#Lets just collect all of them
$ReconciliationUsers = Get-MsolUser -All -LicenseReconciliationNeededOnly | Select-Object UserPrincipalName, IsLicensed, LicenseReconciliationNeeded, Licenses

$Params = @{UserPrincipalName=[STRING];MailboxCreatedON=[DateTime];IsLicensed=[BOOL]$false;LicenseReconciliationNeeded=[BOOL]$false;DaysUnlicensed=[INT]-1;HasAnyExcLicense=[BOOL]$False}

#We are interested only on Unlicensed mailboxes, If the MailboxCreatedON is blank, there is no mailbox provisioned for this user on Online.
#The script outputs everything in to the CSV file, Users can filter on MailboxCreatedON field for filtering Unlicensed mailboxes.

Foreach($User in $ReconciliationUsers){
 #Create the Custom object to store the results
 $UserObj = New-Object -TypeName PSCustomObject -Property $Params

 $UserObj.UserPrincipalName = $user.UserPrincipalName
 $UserObj.IsLicensed = $user.IsLicensed
 $UserObj.LicenseReconciliationNeeded = $user.LicenseReconciliationNeeded
 $Userobj.MailboxCreatedON = Get-Mailbox $User.UserPrincipalName -ErrorAction silentlycontinue | Select-Object -ExpandProperty WhenMailboxCreated 
 #If user has mailbox, check how many days it is there unlicensed
 if($UserObj.MailboxCreatedON){
 $UserObj.DaysUnlicensed = ((Get-Date) - $UserObj.MailboxCreatedON).Days
 }
 #Check if there is any Exchange license assgined for this user, if yes, Include it in report
 if( @($user.Licenses).count){
 if($User.Licenses[0].ServiceStatus | Where-Object {$_.serviceplan.servicetype -eq 'Exchange' -and $_.ProvisioningStatus -eq 'Success'}){
 $UserObj.HasAnyExcLicense = $true
 }
 }
 #Append the output to the file as the script collects it
 $UserObj | Export-Csv $Outfile -Append -NoTypeInformation
}
Comments (1)

Skip to main content