Wednesday, August 20, 2008

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.


No comments: