Skip to main content

Creating a Digest of the Quarantine Mailbox Messages on Exchange 2007 using the Logs or Using Exchange Web Service from Powershell.

One of the shiny new features of the Exchange 2007 Antispam agents is the ability to set a quarantine threshold and have any messages that get a SCL value that matches or exceeds that threshold sent to a Quarantine Mailbox where they can be evaluated and either released or deleted by an Administrator. One of the stock features of a lot antispam systems these days is the ability to digest the quarantine folder/mailbox and then send a digest of the quarantined messages to Admins or users on a scheduled basis. This relieves the need to constantly check this mailbox to see what has been quarantined and allows you to easily spot and take action on any false positives that might happen. I’ve come up with 2 methods of doing this on Exchange 2007 for creating Admin digests. For User digests this method could also be used but before you go down that track you should really have a automated release mechanism for the users. This could be done using a small ASP application and using Exchange Web Services but I’m not 100% sure on the resend side whether you could reproduce the Outlook 2007 functionality that allows you to resend a Quarantined message using EWS. If you can’t you could always use a workaround such as grabbing the message as an EML and then submitting it to the Replay directory on a Hub Server. But as I haven’t yet built that I’ll stick to the Admin Digests for now.

The two methods I came up with are as follows the first method uses the Agent logs file using the get-agentlog Exchange Powershell cmdlet which allows you to get which messages where quarantined and then to get the Subject of the message that’s been quarantined (because the agent logs dont record this detail) the MessageTracking logs are also used. To relate the Agent-Logs to the TrackingLogs the MessageID is used. The second method uses Exchange Web Services instead to connect to the Quarantine Mailbox and query for the NDR’s of the quarantined messages during a specific time period. The advantage of the second method is that you can also include Outlook Links to the quarantined messages so if you have the quarantine mailbox as an additional mailbox in Outlook when you click the hyperlinked subject it will open the appropriate Quarantined Message in Outlook (this method could also be adapted to create OWA links instead). With the first method it’s just a static list.

Method 1 Detail

The first part of this script sets up a Hashtable and collection to hold the result of two log queries and relate then together. The Servername and time objects sets the parameters for both the Log queries. The first log query is for the Agent Log which filters the Agent logs to only return Quantined message using the following line.

et-agentlog -StartDate $dtQueryDT -EndDate $dtQueryDTf where { $_.Action -eq "QuarantineMessage"} foreach-object {

The script then loops though the results and populates a hashtable with a key value set to the messageID and the value set to a custom Powershell object. The next section does a message tracking log query using the same DateTime parameters and checks the hashtable to see if the message has been quarantined and if it finds a quarantine message it adds the extra information to the custom object properties thats stored in the Value of the HashTable. The last section of the script loops back through the hashtable and builds a HTML message body using the detail stored in the custom object and then uses the system.net.mail class to send the message over port 25 to the configured email address. You need to be carefull that its possible that the digest message will get quarantined again because your reusing the subjects of the orginanally quantined messages.

This script is designed to be run from a scheduled task so i've included code to add the Exchange Management Shell addin to a normal Powershell session. To use this script you need to set the servername varible in the following line.

$servername = "servername"

As well as the email address details for who the digest message will come from and go to

$MailMessage.To.Add("user@domain.com")
$MailMessage.From = "Digest@domain.com"

Method2 Detail

As mentioned method 2 uses Exchange Web Services to first connect to the Quarantined mailbox and Query for all the Quarantined messages in the specified folder. To allow this to be done easily from Powerhshell I’ve included it in the helper library I’ve been working on and using over the past couple of months. There is some complexity when using this library but it does make using EWS from Powershell easy it’s a bit of a tradeoff is some regards.

To use the library you first need to first load the dll

[void][Reflection.Assembly]::LoadFile("C:\temp\EWSUtil.dll")

Then you can use the objects that are defined within this class library the first thing you need to do is create a EWS connection (this is a custom class i've created within the Class library that contains a Exchange Service Binding for EWS). The parameters you pass into the object creation affect what authentication is used (eg impersonation or delegation) and also whether autodisover is used or not. I haven't included any overloads this time as i didn't really have the time so all the parameters are mandatory you just need to pass in $null if you don't want to use some. So the parameters for the EWSConnection object.

$casURL = "https://" + $servername + "/EWS/Exchange.asmx"
$ewc = new-object EWSUtil.EWSConnection("user@domamain.com",$false, "username", "password", "domain", $casURL )

  • This is the email address of the mailbox you want to create the feed from.
  • This is a boolean that indicates whether you want to use EWS impersonation to access the mailbox you specified in 1. If this is set to $false then delegate access is used.
  • This is the username to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  • This is the password to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  • This is the domain to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  • This is the URL for the CAS server to use if you set this to $null the library will try to use autodiscover to find a CAS server URL. (this isn't site aware)

Once you create the EWSConnection you need to create a Duration object that sets the start and Endtime of recieved messages you want to include in the digest. eg the follow creates a duration that will get all the messages in the last 24 hours.

$drDuration = new-object EWSUtil.EWS.Duration
$drDuration.StartTime = [DateTime]::UtcNow.AddDays(-1)
$drDuration.EndTime = [DateTime]::UtcNow

Then you need to create the object that defines which folder to search generally you will be searching the inbox folder so something like this


$dTypeFld = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::inbox

If your using Delegate mailbox access (as apposed to Impersonation) you need to also set the Mailbox element of the folderid. This allows you to access the folder of a mailbox other then the account your using to authenticate with (as long as you have been given access).

$mbMailbox = new-object EWSUtil.EWS.EmailAddressType
$mbMailbox.EmailAddress = $mbMailboxEmail
$dTypeFld.Mailbox = $mbMailbox

You then need to add the folder to a FolderArray which is needed for the search method this is done for a flexablity in the library.

$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $dTypeFld

The next line executes the GetNDRs method in the library which does a Finditem operation on the folder you specified and returns a generic list of all the items found by the finditem operation.

$NDRList = $ewc.GetNDRs($fldarry, $drDuration)

Because the Original Sender and Reciver are stored on the message in Mapi properties the method retrieves these as Additional properties. Because the Sender address can be either a SMTP or EX (or other type of address) I've include the SenderAddressType and SenderAddressName in the response. The next section of the script builds the body of the digest message using the properties from the NDR method. To create Outlook Links the EWS convertid operation is used to convert the EWSid to the HexEntryID which can be used in an Outlook link.

To send the message I've also included a method to Send the message via EWS also to do this you need to pass in the EmailAddress object array the subject and the body that was built.

To use this script you need to configure the email address of the quarantine mailbox in the following line

$mbMailboxEmail = "quantinemailbox@domain.com"

You also may need to configure the following line depending on your needs by default the script will try to use delegate access and Autodiscover to find the Cas server but it you wanted to say run this script from a machine that wasn't a member a domain and you wanted to specify the username and password to use and the cas server to use then you would need something like

$casURL = "https://servername/EWS/Exchange.asmx"
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$false, "username","password","domain,$casURL )

You also need to configure the recipient you want the digest to go to

$mrMailRecp.EmailAddress = "user@domain.com"

To use this code you need the EWSUtil library which you can download from here (this also includes the source if you which to cut and complile your own code). I've put a copy of the two sample scripts here.


Popular posts from this blog

Testing and Sending email via SMTP using Opportunistic TLS and oAuth in Office365 with PowerShell

As well as EWS and Remote PowerShell (RPS) other mail protocols POP3, IMAP and SMTP have had OAuth authentication enabled in Exchange Online (Official announcement here ). A while ago I created  this script that used Opportunistic TLS to perform a Telnet style test against a SMTP server using SMTP AUTH. Now that oAuth authentication has been enabled in office365 I've updated this script to be able to use oAuth instead of SMTP Auth to test against Office365. I've also included a function to actually send a Message. Token Acquisition  To Send a Mail using oAuth you first need to get an Access token from Azure AD there are plenty of ways of doing this in PowerShell. You could use a library like MSAL or ADAL (just google your favoured method) or use a library less approach which I've included with this script . Whatever way you do this you need to make sure that your application registration  https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.