Wednesday, September 07, 2011

Create Search Folders using the EWS Managed API in a Mailbox or Archive Store using Powershell

Search folders are one of the suite of Exchange search options you can use programatically or to provide users with different views of their mailbox data in Outlook or OWA. Essentially a search folder is like a regular mailbox folder, except that it contains only links to messages in other folders that meet the criteria set in the search filter restriction which means that search folders are great for nonchanging, nondynamic queries. Search folders like Search Filters make use of restrictions in Exchange rather then using the Content Index which means that this comes along with some compromises ,if you are going to use search folders (or a lot of search filters) defiantly have a read of  http://technet.microsoft.com/en-us/library/cc535025%28EXCHG.80%29.aspx .

To create Search Folders using the EWS Managed API its pretty simple for search folders to be visible in Outlook you need to use the SearchFolders WellKnownFolderName enumeration which in a Mailbox relates to the Finder folder in the Non_IPM_Subtree.

Eg the following will create a Search folder to find Items greater than 5 MB in a Mailbox

$MailboxName = "user@domain.com"

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

$service.Credentials = New-Object System.Net.NetworkCredential("user@domain.com","password")
$service.AutodiscoverUrl($MailboxName ,{$true})

$svFldid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SearchFolders,$MailboxName)
$SearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size, (5*1024*1024));
$SearchFolder = new-object Microsoft.Exchange.WebServices.Data.SearchFolder($service);
$searchFolder.SearchParameters.RootFolderIds.Add([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot);
$searchFolder.SearchParameters.Traversal = [Microsoft.Exchange.WebServices.Data.SearchFolderTraversal]::Deep;
$searchFolder.SearchParameters.SearchFilter = $SearchFilter
$searchFolder.DisplayName = "Bigger then 5 MB";
$searchFolder.Save($svFldid);

If you want to create a Search folder in the Archive Store there's no enumeration for the SearchFolders root folder in the Archive Store so you first need to find the Finder folder in the Archive Store then you can use this when you create the Search Folder for example the following will create a Search folder to find Items greater than 5 MB in the Archive Store.

$MailboxName = "user@domain.com"

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

$service.Credentials = New-Object System.Net.NetworkCredential("user@domain.com","password")
$service.AutodiscoverUrl($MailboxName ,{$true})

$FolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1);
$fsf = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName, "Finder");
$ffResult = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveRoot,$fsf, $FolderView)

$SearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size, (5*1024*1024));
$SearchFolder = new-object Microsoft.Exchange.WebServices.Data.SearchFolder($service);
$searchFolder.SearchParameters.RootFolderIds.Add([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveMsgFolderRoot);
$searchFolder.SearchParameters.Traversal = [Microsoft.Exchange.WebServices.Data.SearchFolderTraversal]::Deep;
$searchFolder.SearchParameters.SearchFilter = $SearchFilter
$searchFolder.DisplayName = "Bigger then 5 MB";
$searchFolder.Save($ffResult.folders[0].Id);





12 comments:

Jeff said...

Is there a way to connect to a pre-existing search folder to view properties like item count? I can do this for regular folders, but not search folders.

Thanks,
Jeff

Jeff said...

I found it! One of your posts on technet, actually. I modified the query and bit and output totalitems instead of deteling.

http://tst.social.technet.microsoft.com/Forums/en-US/exchangesvrdevelopment/thread/426a37c8-f1dd-4438-a081-e2b0e378bc5a

Regards,
Jeff

darkstar3d said...

Glen, is there a way to have a folder update at a specified time interval? For instance, I've created a folder for items older than 45 days, but it doesn't update.

I used the DateTimeReceived and used [DateTime]::Today.AddDays(-45) as the search filter.

Thanks

Jeff Girotto said...

Same question! :) I want my search folder dates to move with time, so that older than X is valid every day and not just the day I deploy it.

Patrick said...

Hi Glen. I need your help desperately. I am trying to modify your code to create a search filter based on the name of a category. For example, I want to create a search folder that search's for all email categorized under the "Bills" category. Is this possible? If not, Can I at least search for the mapi properties of a message which define the category? Thank you!

Glen Scales said...

I don't think this will work with EWS, because the category field is a MultiValued String (or String Array) you can't use a IsEqual restriction and you also cant use Contains (because its not String). EWS only implements a subset of what you can do with a MAPI restriction so the only way I think you could do this would be to create it using MAPI eg using something like redemption http://www.dimastr.com/redemption/rdo/RDOSearchFolder.htm .

Cheers
Glen

Michael DeGrushe said...

When trying to create the Large Msg search folder in the Archive mailbox I receive the following error. I think it is because it cannot find the Finder folder. Any ideas on what I can use to set this for the Archive folder?

Exception calling "FindFolders" with "3" argument(s): "The specified folder could not be found in the store."
At C:\scripts\EWSFolders\CreateArchiveLargeSearchFolder.ps1:13 char:2
+ $ffResult = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnown ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ServiceResponseException


FolderName Mailbox UniqueId ChangeKey
---------- ------- -------- ---------
ArchiveMsgFolderRoot
Cannot index into a null array.
At C:\scripts\EWSFolders\CreateArchiveLargeSearchFolder.ps1:21 char:2
+ $searchFolder.Save($ffResult.folders[0].Id);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Glen Scales said...

Try https://www.dropbox.com/s/knl3h0ng02d0dl6/largeArchivesf.zip?dl=0 this works okay for me with the latest Managed API.

Cheers
Glen

Michael DeGrushe said...

Glen,Thanks for the new script. It worked perfectly!

Michael

Bert said...

Hello Glen,

We are doing away with our archive mailboxes and are importing them back into the primary mailbox. Some users created searchfolders within those archives.
I have been looking for a way to find the configuration of a searchfilter on a searchfolder with EWS and powershell. The idea is to recreate the same filter in his primary mailbox.
If you would be able to put me on my way I would be eternally grateful :-)

Glen Scales said...

EWS has only a subset of the Search Filter Outlook has so depending on the complexity of the Filter it may or may not be possible to map what was done in Outlook, Also the underlying Id for folders etc won't have any meaning in the Primary Mailbox so there would be a lot of translation of existing data structures that would be needed. What your trying to do is quite difficult because of all the variables involved

Bert said...

Hello Glen,

Thanks for looking into it, I was afraid I was missing something obvious :-)

Bert