Tuesday, October 13, 2009

Moving Items into their own folder by a date range using the EWS Managed API and Powershell (attempting to reduce mailbox clutter)

Most people these days are on track to endless mailbox clutter based on an exponentially increasing number of email items that arrive every day. Also Shared mailboxes can soon become an administrative nightmare if mailbox content isn’t actively maintained and archived. One small way of trying to claw back some of this affect is to write a script that can organize messages into their own folder based on certain parameters. In Exchange 2007 (and now 2010) you can use the EWS Managed API to do this reasonably easy. You just need to split this up into some easy chunks of functionality that you can logically splice together.

The first chunk is some code that will find the objects in question that you want to move in the EWS Managed API this involves setting up an appropriate search filter.

Because I want to use a date range this means I need two search criteria meaning you have to use a searchfiltercollection with an AND logical operator. Sounds a little complicated but not really first we need to create the collection

$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);

Next we need to create two searchfilter objects that will cover the date ranges when want to include in the search (I’ve defined the date as the previous day eg.)

$StartDate = [system.DateTime]::Today.AddDays(-1)
$EndDate = [system.DateTime]::Today

$Sfgt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $StartDate)
$Sflt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $EndDate)

Then all you need to do is add these objects to the serachcollection

$sfCollection.add($Sfgt)
$sfCollection.add($Sflt)

The next thing we need to do is create a folder for the item to go into which is pretty easy Im just going to save it as a sub folder of the inbox and name it after the current date

$NewFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
$NewFolder.DisplayName = $EndDate.ToString("yyyy-MM-dd")
$NewFolder.Save($InboxFolder.Id.UniqueId)

Now it just a matter of going through the items in the result set and move these to the folder we just created

foreach ($miMailItems in $frFolderResult.Items){
$miMailItems.Subject.ToString()
$miMailItems.Move($NewFolder.Id.UniqueId)
}

I put a download of this code here the complete script itself looks like the following it basically uses the currently logged on user to access the mailbox configured via the email address.

$MailboxName = "user@domain.com"
$StartDate = [system.DateTime]::Today.AddDays(-1)
$EndDate = [system.DateTime]::Today

$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$sidbind = "LDAP://<SID=" + $windowsIdentity.user.Value.ToString() + ">"
$aceuser = [ADSI]$sidbind

$service.AutodiscoverUrl($aceuser.mail.ToString())

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
$Sfgt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $StartDate)
$Sflt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $EndDate)
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
$sfCollection.add($Sfgt)
$sfCollection.add($Sflt)
$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000)
$frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
$NewFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
$NewFolder.DisplayName = $EndDate.ToString("yyyy-MM-dd")
$NewFolder.Save($InboxFolder.Id.UniqueId)
foreach ($miMailItems in $frFolderResult.Items){
"Moving" + $miMailItems.Subject.ToString()
[VOID]$miMailItems.Move($NewFolder.Id.UniqueId)
}

4 comments:

Steven said...

One of the projects I'm working on is trying to make a 'better' export-mailbox tool that is used to actually delete mail that uses the search indexes on the server to find the targeted mail in question (like what exmerge could do). What I haven't been able to figure out is how to search an entire mailbox as opposed to using a WellKnownFolderName that points statically to the Inbox folder, for example. Any thoughts on how to search an entire mailbox?

IF said...

Hi,

I can see that post is very old, but I have one qustion regarding your script. Is it possible to use same approach to move items from Managed Folders back to their original location, inbox, sent items etc...

Thank you
Ivan

Anonymous said...

Only if you have a spreadsheet somewhere telling you what the original locations were. Outlook and Exchange do not retain this information.

Anonymous said...

I should amend that by saying you could sort to the default folders by item type (IPM.Appointment, IPM.Note, etc.) and check for the IsFromMe property on IPM.Notes for Sent Items. So I guess it depends what you consider the "original" location.