Wednesday, August 27, 2008

Come see me Live at Tech-Ed Australia next Friday

If your going to Tech-Ed in Australia next week I'll be doing a 30 minute presentation in the MVP theatre https://aunz.msteched.com/au/public/mvptheatre.aspx mine is MVP401 9:15-9:45 on Friday 5th.

I'm going to be covering how you can go about building Advanced Exchange Management Shell Powershell scripts and also covering crossover scripting with Exchange Web Services and EMS and all the other stuff you see covered on this blog. For the last 5 minutes if the demo gods allow I'm going to do a 5 minute OWA custom form tv chef demo.

There's a maximum of 20 seats in the theatre so hopefully I'll have a few takers the one thing i can promise is some unique content you wont see anywhere else so come pay me a visit if your interested.



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.


Monday, August 11, 2008

Accessing the NON_IPM_Subtree folders in Exchange Web Services

One important and potentially frustrating point for anybody who wants to set permissions on the calendar folder in a mailbox with any Exchange API that allows you to modify the folder DACL’s is that you also need add the same ACE your adding (or modifying) to the FreeBusy Data folder which is one the NON_IPM_Subtree Root folders in a mailbox explained in this KB. Exchange Web Services is no exception to this rule and accessing the NON_IPM_Subtree folders isn’t as straight forward as normal mailbox folders but here’s a method for accessing the freebusy folder and modifying the DACL.

The highest level you can get to easily with EWS is the DistinguishedFolderIdNameType.msgfolderroot which in the basically the Root of the IPM_Subtree if you get the parentfolderID of this folder using a GetFolder Operation this will represent the Root of the NON_IPM_Subtree you can then use this ID in FindFolder operation to discover all the NON_IPM_Subtree folder in this case I’ve used a restriction to find the Free Busy folder based on the folderName. Then once you have the FolderID of the Free Busy folder you can Get and Set the ACL via the normal GetFolder and Update Folder operations.

Okay enough said I’ve put a download of the code here the code itself looks like

PermissionSetType fbPermissionSet = new PermissionSetType();
BaseFolderIdType[] bfBaseFolderArray = new BaseFolderIdType[1];
DistinguishedFolderIdType dfRootFolderID = new DistinguishedFolderIdType();
if (intebExchangeServiceBinding.ExchangeImpersonation == null)
{
EmailAddressType mbMailbox = new EmailAddressType();
mbMailbox.EmailAddress = emEmailAddress;
dfRootFolderID.Mailbox = mbMailbox;
}
dfRootFolderID.Id = DistinguishedFolderIdNameType.msgfolderroot;
bfBaseFolderArray[0] = dfRootFolderID;
ListrfRootFolder = GetFolder(bfBaseFolderArray);
BaseFolderIdType[] bfIdArray = new BaseFolderIdType[1];
bfIdArray[0] = rfRootFolder[0].ParentFolderId;
List fbFolder = FindFolder(bfIdArray, "Freebusy Data");
FolderResponseShapeType frFolderRShape = new FolderResponseShapeType();
frFolderRShape.BaseShape = DefaultShapeNamesType.AllProperties;
GetFolderType gfRequest = new GetFolderType();
gfRequest.FolderIds = new BaseFolderIdType[1] { fbFolder[0].FolderId };
intFreeBusyFolderID = fbFolder[0].FolderId;
gfRequest.FolderShape = frFolderRShape;
GetFolderResponseType gfGetFolderResponse = intebExchangeServiceBinding.GetFolder(gfRequest);
FolderType cfCurrentFolder = null;


if (gfGetFolderResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success)
{
cfCurrentFolder = (FolderType)((FolderInfoResponseMessageType)gfGetFolderResponse.ResponseMessages.Items[0]).Folders[0];
fbPermissionSet = cfCurrentFolder.PermissionSet;
}

public List GetFolder(BaseFolderIdType[] biArray)
{
try
{
List rtReturnList = new List();
GetFolderType gfGetfolder = new GetFolderType();
FolderResponseShapeType fsFoldershape = new FolderResponseShapeType();
fsFoldershape.BaseShape = DefaultShapeNamesType.AllProperties;
DistinguishedFolderIdType rfRootFolder = new DistinguishedFolderIdType();
gfGetfolder.FolderIds = biArray;
gfGetfolder.FolderShape = fsFoldershape;
GetFolderResponseType fldResponse = intebExchangeServiceBinding.GetFolder(gfGetfolder);
ArrayOfResponseMessagesType aormt = fldResponse.ResponseMessages;
ResponseMessageType[] rmta = aormt.Items;
foreach (ResponseMessageType rmt in rmta)
{
if (rmt.ResponseClass == ResponseClassType.Success)
{
FolderInfoResponseMessageType firmt;
firmt = (rmt as FolderInfoResponseMessageType);
BaseFolderType[] rfFolders = firmt.Folders;
rtReturnList.Add(rfFolders[0]);
}
else
{
//Deal with Error
}

}
return rtReturnList;
}
catch (Exception exException)
{
Console.WriteLine(exException.ToString());
throw new GetFolderException("Get Folder Exception");
}

}
public List FindFolder(BaseFolderIdType[] biArray, String fnFolderName)
{
try
{
List rtReturnList = new List();
FindFolderType fiFindFolder = new FindFolderType();
fiFindFolder.Traversal = FolderQueryTraversalType.Shallow;
FolderResponseShapeType rsResponseShape = new FolderResponseShapeType();
rsResponseShape.BaseShape = DefaultShapeNamesType.AllProperties;
fiFindFolder.FolderShape = rsResponseShape;
fiFindFolder.ParentFolderIds = biArray;
RestrictionType ffRestriction = new RestrictionType();
IsEqualToType ieToType = new IsEqualToType();
PathToUnindexedFieldType fnFolderNameField = new PathToUnindexedFieldType();
fnFolderNameField.FieldURI = UnindexedFieldURIType.folderDisplayName;

FieldURIOrConstantType ciConstantType = new FieldURIOrConstantType();
ConstantValueType cvConstantValueType = new ConstantValueType();
cvConstantValueType.Value = fnFolderName;
ciConstantType.Item = cvConstantValueType;
ieToType.Item = fnFolderNameField;
ieToType.FieldURIOrConstant = ciConstantType;
ffRestriction.Item = ieToType;
fiFindFolder.Restriction = ffRestriction;

FindFolderResponseType findFolderResponse = intebExchangeServiceBinding.FindFolder(fiFindFolder);
ResponseMessageType[] rmta = findFolderResponse.ResponseMessages.Items;
foreach (ResponseMessageType rmt in rmta)
{
if (((FindFolderResponseMessageType)rmt).ResponseClass == ResponseClassType.Success)
{
FindFolderResponseMessageType ffResponse = (FindFolderResponseMessageType)rmt;
if (ffResponse.RootFolder.TotalItemsInView > 0)
{
foreach (BaseFolderType suSubFolder in ffResponse.RootFolder.Folders)
{
rtReturnList.Add(suSubFolder);
}

}
else
{
throw new FindFolderException("Find Folder Exception No Folder");
}
}
else
{
throw new FindFolderException("Find Folder Exception Seach Error");
}

}

return rtReturnList;
}
catch (Exception exException)
{
Console.WriteLine(exException.ToString());
throw new FindFolderException("Get Folder Exception");
}
}

Saturday, August 02, 2008

Using Exchange Web Services to Enhance Exchange Message Tracking

Message Tracking from an operation perspective is one of the most useful windows you have into what's happening on your Exchange server. Used skillfully these logs can tell you firstly that your server is operating correctly eg you are receiving messages and people are sending messages, how much data is coming in, going between, and going out of your network via Email. And when the inevitable problem happens and a message is delayed or not delivered message tracking becomes one of the most import tools for diagnosis.

But Message tracking logs are only a fraction of the information that is contained on a message at the point of time it was traversing the Transport pipeline. But if you combine Message Tracking with an Exchange Store API like Exchange Web Services you suddenly have a very powerful tool that can give you an unlimited amount of flexibility to audit,analysis and take action on your environment. Okay I maybe getting a little over enthusiastic but this is one area i think is a little underutilized because of a little snow blindness you have your sysadmins in one camp who's realm is Exchange Message Tracking and you have a number of good products that can produce increasing complex reports. And the other side your developers who take advantage of the store Api to build item based applications and content and archiving applications. If you bring these two worlds together we can start using the Message Tracking logs as an active tool that can help both sides of this equation for example you see a large message in your tracking logs that your worried may breach an important company policy. Now typically in the Tracking logs you would have the size of the message and subject and who sent it along with one other important piece of information the Internet MessageID. Typically now if an admin wanted to check that message they would need assign an account rights to the mailbox that send or received the message and then using outlook open the mailbox and locate that message in the users mailbox which is a time consuming process. Exchange Web Services has a number of key things that can help simplify this process.
  • AutoDiscover : You can use the Email Address in the case of Message Tracking the Sender or Recipient Address to find the correct URL to use for EWS to open/query the mailbox that sent of received the message recorded in the log
  • Impersonation : Impersonation allows you to Impersonate the Sender or Receiver of the message to access their mailbox and get the content. Impersonation rights can be granted at the Store or Mailbox level and alleviates the need to grant mailbox rights to an accounts to access mailboxes on a ad hoc basis
  • Soft Deleted Traversal: If the user has deleted the item after they have sent or received the message then the message will be located in the dumpster of one of the folders in the Mailbox. A Soft deleted traversal can be used to search the dumpster of a folder to find the item. A limitation of EWS is that you can't actually access the item in the dumpster so another API such as MAPI needs to used to recover/export the item if necessary.
As i mentioned before the Interlink between the Message tracking Logs and the Exchange store is the Internet messageID. To find a message based on the Internet messageID we need to do a search of a mail folder using the Message ID as restriction. But its not as easy as that unfortunately because many things can happen to a message once its sent or received such as the mailbox rules or the user themselves moving it to another folder or the user deleting the message either doing a shift delete or using the normal deleted Items method. So it maybe necessary to do a recursive search of every folder in a mailbox and if that turns up nothing you may need to do a dumpster dive of every folder in a mailbox using a soft deleted Traversal.

What are the catches to this method firstly in Exchange one user can send as another if they have permissions and in this case although the log files will reflect the sender address as the mailbox that sent the item that actual sent email will be located in another mailbox. Also the dumpster dive method doesn't account for messages in deleted folders.

Okay let get to some EWS code to search the inbox for a particular message ID for those non developers out there hang in their I'll give you a powershell method later.


FindItemType fiFindItemRequest = new FindItemType();
fiFindItemRequest.Traversal = ItemQueryTraversalType.Shallow;
ItemResponseShapeType ipItemProperties = new ItemResponseShapeType();
ipItemProperties.BaseShape = DefaultShapeNamesType.IdOnly;
fiFindItemRequest.ItemShape = ipItemProperties;

DistinguishedFolderIdType dfDfolder = new DistinguishedFolderIdType();
dfDfolder.Id = DistinguishedFolderIdNameType.inbox;
DistinguishedFolderIdType[] faFolderIDArray = new DistinguishedFolderIdType[1];
faFolderIDArray[0] = new DistinguishedFolderIdType();
faFolderIDArray[0] = dfDfolder;
fiFindItemRequest.ParentFolderIds = faFolderIDArray;
Restriction Type ffRestriction = new Restriction Type();
IsEqualToType ieToType = new IsEqualToType();
PathToUnindexedFieldType miMessageID = new PathToUnindexedFieldType();
miMessageID.FieldURI = UnindexedFieldURIType.messageInternetMessageId;

FieldURIOrConstantType ciConstantType = new FieldURIOrConstantType();
ConstantValueType cvConstantValueType = new ConstantValueType();
cvConstantValueType.Value = "messageid@domain...";
ciConstantType.Item = cvConstantValueType;
ieToType.Item = miMessageID;
ieToType.FieldURIOrConstant = ciConstantType;
ffRestriction.Item = isotype;
fiFindItemRequest.Restriction = ffRestriction;
FindItemResponseType frFindItemResponse = esb.FindItem(fiFindItemRequest);

Okay hopefully i haven't lost anyone after that but to make this useful for those that can't put the above code into Visual Studio I've wrapped up a modified version of the above code as well as another method to Recurse ever folder in a mailbox,cater for dumpster diving, exporting to EML and also download attachments into a Class library (DLL) that can be loaded and used with a few extra lines in Powershell. So as a Sysadmin with this you can do things like use Message tracking to find messages that where over 5 MB today and then pipe them into this DLL to download all the attachments or export the messages to a directory. Or just produce a more detailed report on the content of the messages and attachments. Or export all the communication between a particular domain or on a particular subject. Or pretty much anything else you can put your mind to (or all those crazy requests you get from HR department).

To do a recursive search of a mailbox with EWS you need to issue a separate Finditems request for each folder as there is no Deep Traversal for find items. To get the Folder hierarchy you can however do a Deep Traversal which will return a list of all folders within a Mailbox. Matt Stehle recently posted this which describe how to get the best performance out of this type of operation.

This library supports Autodiscovery based on the Email address you enter and also support both Impersonation and Delegation authentication models. It also supports hardcoding the username, password and domain as well as the EWS/CAS URL which allows you to use the library from a remote machine anywhere in the network without it needing to be a member of the domain.

To use the library from powershell you basically need to first load the dll eg

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

Then you can use the objects that are defined within this class 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 objects creation affect what authentication is used (eg impersonation or delegation) and also whether autodiscover is used or not. I haven't included any overloads 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)


The library returns a generic List of objects that matchs the restiction which depending on the code you use can be the messageID or to be as multi use as possible you can use any of the unindexed properties such as the Subject Etc.

The following is a sample that uses firstly the get-messagetrackinglogs cmdlet to retrieve the MessageID for any messages that are over 5 MB that have been recieved today in the last 2 hours. Then it looks at the recipient address and verfies that they are local accounts and then uses get-user to make sure you are working with the Primary Email address for the mailbox and not a proxy address which would make this script fail. It then uses Impersonation and the currently logged on user credentials to search for the message in the inbox of the recipient and exports the message and any attachments to c:\export\message and c:\export\attachments respectivly.

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

function getMessage($recpAddress){
$recp
$ascii = new-object System.Text.ASCIIEncoding
$ewc = new-object EWSUtil.EWSConnection($recpAddress,$true, "", "", "",$null)
$dType = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dType.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::inbox
$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $dType
$randNumber = New-Object system.random
$prop = new-object EWSUtil.EWS.PathToUnindexedFieldType
$prop.FieldURI = [EWSUtil.EWS.UnindexedFieldURIType]::messageInternetMessageId
$messagelist = $ewc.FindMessage($fldarry,$prop,$_.MessageID,$false)
foreach ($message in $messagelist){
$baByteArray = [Convert]::FromBase64String($message.MimeContent.Value)
$emlMessage = $ascii.GetString($baByteArray)
$emlfile = new-object IO.StreamWriter(("c:\export\message\" + $message.Subject.Replace("#","").Replace(":","") + $mc+ ".eml"),$true)
$emlfile.WriteLine($emlMessage)
$emlfile.Close()
"Exported Message " + $message.Subject
$mc = $mc +1
if ($message.hasattachments){
"Exported Message to " + ("c:\message\" + $message.Subject + $mc + ".eml")
foreach($attach in $message.Attachments){
$ewc.DownloadAttachment(("c:\export\Attachments\" + $randNumber.next(1,1000) + $attach.Name.ToString()),$attach.AttachmentId);
"Downloaded Attachment : " + $attach.Name.ToString()
}
}
}
}


$servername = "servername"
$DomainHash = @{ }

get-accepteddomain ForEach-Object{
if ($_.DomainType -eq "Authoritative"){
$DomainHash.add($_.DomainName.SmtpDomain.ToString().ToLower(),1)
}

}

$dtQueryDT = [DateTime]::UtcNow.AddHours(-2)
$dtQueryDTf = [DateTime]::UtcNow
Get-MessageTrackingLog -Server $servername -ResultSize Unlimited -Start $dtQueryDT -End $dtQueryDTf -EventId "RECEIVE" where {$_.TotalBytes -gt 5242880} ForEach-Object{
foreach($recp in $_.recipients){
if ($recp -ne ""){
$recparray = $recp.split("@")
if ($DomainHash.ContainsKey($recparray[1])){
$vuser = get-user $recp
getMessage($vuser.WindowsEmailAddress)
}
}
}}

To make this script search the whole mailbox instead of just the inbox you would need to change the following two lines,

first you need to set the parent folder to the root of the mailbox

$dType.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::msgfolderroot

And change the following line to RecurseFolder

$messagelist = $ewc.RecurseFolder($fldarry,$prop,$_.MessageID,$false)

If you wanted to do a dumpsterdive change the last parameter to $true

I'm really just scratching the surface of the cool things you can do with this and i could keep rabbiting on for days but I've got bring this to end. I've put a download of the lastest verion of the library along with the source for those that want to roll your own here the sample script can be downloaded from here. As i mentioned before this library is untested and highly experimental and only sutible for test enviroments.