User’s unable to delete mail from their mailbox

Do you have an issue, where your users are unable to delete mail from their mailboxes? Does the mail being deleted, disappear from their mailbox and reappear? Does your Organization make user of “Retention Policies” from Microsoft Purview?

If your answer is yes to all 3 of the above questions, then your users “TotalDeletedItemSize” has most likely reach the size quota (the quota for this sub-folder is 100GB).

You can check this, either directly for an individual user or by exacting a list of all users.

If you want to do this directly on an individual user, you can make use of the below script


get-mailbox [email protected] | Get-MailboxStatistics  | select DisplayName,ItemCount,TotalItemSize,DeletedItemCount,TotalDeletedItemSize 

If you want to pull a list of all user, into a csv file, you can make use of the below script. Please make sure to update the location of where toy want the csv file to be exported to.


get-mailbox -ResultSize unlimited | Get-MailboxStatistics  | select DisplayName,ItemCount,TotalItemSize,DeletedItemCount,TotalDeletedItemSize | export-csv C:\scripts\UsersRetentionSizes.csv -NoTypeInformation -Append

Once you have this information and have found the users that have reached the 100GB size quota, on their “TotalDeletedItemSize” folder, you will need to follow the below steps to remove the Retention Policy being applied to their mailbox. Once the Retention Policy has been removed from the user, the “TotalDeletedItemSize” folder will start to be cleared.

Please note that this is not immediate and can take up to 30 days for the Retention Policy to stop applying to the user, but there are ways to speed this up.

I followed the below article from Microsoft to remove the Retention Policy from the user, but they failed to mention 1 key command that forces the process to beginning. Without forcing the process to start, you will be waiting 30 days or more for the “TotalDeletedItemSize” folder to be cleaned up.

Using the above mentioned article, I have managed to stream line the process. (well at least I think so, as it works for me)

The very first thing you will need to do, is to go to the Microsoft Compliance Portal (also known as Microsoft Purview), then open the “Data lifecycle management” and then the “Retention Policies Tab”.

You will need then need to find and edit the Retention Policy, defined for Exchange Online, and add the required user/users to the “Excluded” option. Once added, I would recommend waiting about an hour or so just for backend M365 replication to take place, but its not necessarily required.

Once the user have been “Excluded” from the Exchange Online Retention Policy, the below Exchange Online PowerShell commands will need to be run. Please be sure to update the command with the required users UPN.


Set-Mailbox [email protected] -SingleItemRecoveryEnabled $false
Set-Mailbox [email protected] -RetainDeletedItemsFor 1
Set-Mailbox [email protected] -LitigationHoldEnabled $false
Set-Mailbox [email protected] -RemoveDelayHoldApplied
Set-Mailbox [email protected] -RemoveDelayReleaseHoldApplied

Once the above commands have been run, the final step is to force the processing to start using the ManagedFolderAssistant

Start-ManagedFolderAssistant [email protected]

You may need to run the above command multiple times, over the course of a few days.

You can now monitor the size of the “TotalDeletedItemSize” folder, but using the previously mentioned PowerShell command for the individual mailbox.

get-mailbox [email protected] | Get-MailboxStatistics | select DisplayName,ItemCount,TotalItemSize,DeletedItemCount,TotalDeletedItemSize

Once the users “TotalDeletedItemSize” folder has reduced down to 0GB or any other acceptable size, it is strongly recommended that you do back to the Exchange Retention policy and remove the used from being Excluded.

We also recommend that you run the below commands to reset the users mailbox setting, back to their original state. This can be done by using the below commands.

Set-Mailbox [email protected] -SingleItemRecoveryEnabled $true
Set-Mailbox [email protected] -RetainDeletedItemsFor 14
Set-Mailbox [email protected] -LitigationHoldEnabled $true

Please note that we take no responsibility for any issues caused by these commands and it is up to YOU to review and ensure that these commands can be run in your environment.

Determine the source site of Outlook clients on Exchange server

We have been toying with the idea of centralising our Exchange environment for a while now, and as part of this project, we needed to audit our Outlook clients, to determine which source site they were connecting from.

I was tasked with this, and was able to quickly gather this information, from both the Exchange 2003 and the 2007 environment, without too much hassle.

For Exchange 2007 simply use get-logonstatistics and select the information that you need. I added some additional spice, which exports each server to a separate CSV file.

foreach ($server in get-mailboxserver){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
Get-LogonStatistics -server $server | select UserName, ClientIPAddress | sort UserName -Unique | Export-Csv $filename

Exchange 2003 is very similar, but as you can probably guess by now, you need to use WMI.

foreach ($server in (Get-ExchangeServer | Where {$_.IsExchange2007OrLater -eq $false})){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
Get-Wmiobject -namespace rootMicrosoftExchangeV2 -class Exchange_Logon -Computer $server | select MailboxDisplayName, ClientIP | sort MailboxDisplayName -Unique | Export-Csv $filename

My job done, I sent the CSV files of the project managers, only to find out that they thought it would be nice, to see exactly which site each IP address belonged to.

This proved to be a little more tricky, but after a few minutes of probing the Interwebs, I found a post where Shay uses nltest to get the site information for a computer.

I assimilated this into my script with a little DNS lookup to find the host name and came up with a function which will retrieve the site information for each IP address on the fly and add that to the CSV file.

function Get-ComputerSite ($ip){
Write-Host "Current IP:" $ip
$site = $null
$computer = [System.Net.Dns]::gethostentry($ip) 
$site = nltest /server:$($computer.hostname) /dsgetsite
Return $site[0]

$ADSiteWMI = @{Name="ADSite";expression={Get-ComputerSite $($_.ClientIP)}}
$ADSite = @{Name="ADSite";expression={Get-ComputerSite $($_.ClientIPAddress)}}

foreach ($server in get-mailboxserver){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
$LogonStats = Get-LogonStatistics -server $server | sort UserName -Unique 
$LogonStats | select UserName, ClientIPAddress, $ADSite | Export-Csv $filename 

foreach ($server in (Get-ExchangeServer | Where {$_.IsExchange2007OrLater -eq $false})){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
$LogonStats = Get-Wmiobject -namespace rootMicrosoftExchangeV2 -class Exchange_Logon -Computer $server | sort MailboxDisplayName -Unique
$LogonStats | select MailboxDisplayName, ClientIP, $ADSiteWMI | Export-Csv $filename

This does take some time to complete on servers with many connections, but it gets the results required. I have already noticed a few issues, and the script can do with a little more refinement.

I will post these updates as soon as I get round to adding them. For now, I hope this script can help someone else with a similar problem.

The complete script can be downloaded from here:

Using SCL to prevent messages from going to Junk Mail

In our environment, we have a number of email addresses which are managed by automated programs and systems and even some home grown applications.

Most of these systems use POP3 to connect to the mailboxes and download incoming email. Obviously POP3 does not give you access to subfolders like “Junk Mail”. It has come to our attention recently, that the Junk email rule has been flagging valid client messages as Junk Mail, and sending these messages to the Junk Mail folder. The result is that these instructions / client information never make it to the back office workflow systems.

To prevent this from happening, you first need to understand SCL or Spam Confidence Level.

The SCL, in a nutshell is basically a score based on a number of criteria, which determine how likely a message is to contain spam. The higher the score (maximum 9) the more confident Outlook is that the message is spam.

An awesome way to view the SCL for individual messages is to install a custom form, which displays an additional column with this information. More information about that here:

After installing the form, I needed to start sending some spam to myself. This would establish the same message is either blocked or cleared by the Transport Rule. I grabbed an obvious spam message from my Gmail account and turned it into a Powershell spambot:

$messageParameters = @{

Subject = "Vicodin ES (Hydrocodone) 650mg x 30 pills $209 -VISA- tbrkl rqg" Body = " -== The Best Painkillers available ==- Buy Hydrocodone, Vicodin ES, Codeine, Phentermin, Norco, Valiuml, Xanaxl Online You pay & we ship, Absolute NO question asked No PrescriptionNeeded (No doctor approval needed!) 100% deliver your order to your house We have been in business since 1998 This is a rare bargain online to obtain these UNIQUE products. No prior order needed. Limited supply of these hard to get pills, so hurry! "

From = "[email protected]"
To = "[email protected]" 
Bcc = "[email protected]" 
SmtpServer = "" 
Send-MailMessage @messageParameters –BodyAsHtml

Confirmed! My spam message was being trapped by the Junk Mail rule with SCL 9 and moved to the Junk Mail folder.

OK, next we needed to create the Transport Rule. Now, if you are new to Powershell / Exchange I would suggest creating the rule in the GUI, as the interface / wizard used in that process is similar to the Outlook rules wizard.

Once you have the rule created it is very easy to add additional addresses using Powershell. More about that later. For the purposes of this post, I will however create the rule using the shell.

$condition = Get-TransportRulePredicate SentTo 
$condition.Addresses = @((Get-Mailbox "*jean.louw*")) 
$action = Get-TransportRuleAction SetSCl $action.SclValue = "-1" 
$warning = "WARNING: Adding mailboxes to this rule will prevent the Junk Mail rule from detecting possible spam." 

New-TransportRule -name "Set SCL level to -1" -Conditions @($condition) -Action @($action) -Comments $warning

This script will create the rule to set the SCL for all messages to matching addresses to -1. You can replace “(Get-Mailbox “*jean.louw*”)” with any expression or command, which will give you the mailboxes you need to add to the rule.

Now that we have the rule in place, we need to confirm that it is working. Yet again, I sent a control “spam” message ala spambot9000.

This time the message SCL was -1, as we predicted, and the message was not moved to Junk Mail as before.

In future, should you need to add additional email addresses to your rule, you can use the following:

$condition = Get-TransportRulePredicate SentTo 
$condition.Addresses = @((Get-Mailbox "*system*")) 
$condition.Addresses += @((Get-Mailbox "*louw, jean*")) 
Set-TransportRule "Set SCL level to -1" -Conditions @($condition)

Remember that you have to add all of your address searches, each time, as the conditions are overwritten by set-transportrule. This is a really easy way to get around the problem of false positives in mailboxes where humans don’t manage mailboxes, and are unable to notice that valid emails are being sent to Junk Mail.