Video Screencast Help
Symantec to Separate Into Two Focused, Industry-Leading Technology Companies. Learn more.
Enterprise Vault Engineering Blog

[PowerShell] MAPI Properties through EWS(Exchange Web Services)

Created: 31 Jul 2014 • Updated: 03 Aug 2014
Shinichi Hayashi's picture
+2 2 Votes
Login to vote

For troubleshooting, we sometime need to dive into MAPI properties.
To do this, we usually use Outlook Spy or MFCMAPI.

They both work great and let you debug the MAPI properties in GUI.
The third approach is use EWS(Exchange WebServices) through PowerShell.

The benefit of using PowerShell to see the properties is that we can work with the data easily.

These are the use cases from my mailbox.

  • Disk space saved by replacing the email with ShortCut. 
PS>$return_object | %{if($_.OriginalSize -gt $_.Size){$_.OriginalSize -  $_.Size}} | Measure-Object -Sum -Maximum|ft Count,Sum,Maximum -AutoSize

Count     Sum Maximum
-----     --- -------
  122 5324215  551692

Total of 5324215 bytes were saved and the maximum was 551692 bytes.

  • Average days Enterprise Vault archived the emails.
PS> $return_object | %{($_.ArchivedDate - $_.DateTimeReceived).Days} | Measure-Object -Average|format-table Count,Average -autosize

Count          Average
-----          -------
  122 25.5081967213115
  • When did the archive run ?
PS > $return_object|Group-Object {($_.ArchivedDate).toShortDateString()}|Sort-Object name -Descending|ft Name,Count  -AutoSize

Name       Count
----       -----
2014/05/08    17
2014/05/07    70
2014/05/06    23
2014/05/05    11
2014/05/02     1
2014/05/01     7
2014/04/30    12
  • Which emails were archived on 2014-05-07?
PS > $return_object | where-object {(($_.ArchivedDate -gt (get-date 2014-05-07)) -and ($_.ArchivedDate -lt (get-date 2014-05-08)))} | format-table DateTimeReceived,ArchivedDate,Sender,Subject -autosize


DateTimeReceived    ArchivedDate        Sender            Subject
----------------    ------------        ------            -------
2014/04/09 21:55:25 2014/05/07 9:43:01  Christopher Loak  New Lab budget
2014/04/09 22:00:59 2014/05/07 9:43:01  Scott Mathews     Feature News
2014/04/09 22:12:18 2014/05/07 9:43:02  Ted Johnson       Re:New Lab budget
..

Once you have the data from EWS, you can sort or filter with any property you like.
Other possible use case are 

  • Search for emals with specifing a property with specif value.
  • Loop through the Folder and watch if any property has changed

 

Let's go through how this is done.

First you need to install "Microsoft Exchange Web Services Managed API".
http://www.microsoft.com/en-us/download/confirmation.aspx?id=35371

Import the Microsoft.Exchange.WebServices.dll to your PowerShell session.

PS>Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"

Then we connect to the Exchange Service.
We have to specify the version of Exchange Version you are connecting.
If you do not know which version, see this article or you can try your luck (There are only 5 versions that support EWS).
http://office.microsoft.com/en-001/outlook-help/determine-the-version-of...

 Exchange2007_SP1
 Exchange2010
 Exchange2010_SP1
 Exchange2010_SP2
 Exchange2013

PS>$exchService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2, [System.TimeZoneInfo]::Local)

Use the default credential to connect to Exchange Server and use your email address to auto discover the EWS access point.

PS > $exchService.UseDefaultCredentials = $true 
PS > $exchService.AutodiscoverUrl("YOUR@EMAIL_ADDRESS")

This part is creating the Filter criteria.
The example is specifying any items with "IPM.Note.EnterpriseVault.Shortcut" ItemClass.

PS > $searchFilterCollection = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or)
PS > $searchFilter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ContactSchema]::ItemClass,"IPM.Note.EnterpriseVault.Shortcut")
PS > $searchFilterCollection.add($searchFilter1)

This part is creating a Protery Set that you are interested in.
Archive Date is a custom Property created by the Enterprise Vault so need to specify the GUID, name and the Type.

PS > $prArchiveDate = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([GUID]"D0F41A15-9E91-D111-84E6-0000F877D428","Archived Date",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime)
PS > $prArchiveDatePropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet($prArchiveDate )
PS > $customPropSet = new-object Microsoft.Exchange.WebServices.Data.PropertySet($prArchiveDatePropertySet)

Add any other properties in interest.

PS > $customPropSet.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject)
PS > $customPropSet.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
PS > $customPropSet.add([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Sender)

This part creates a view of the query result from the EWS.
This is specifying that only 50 items are returned from Exchange so that unexpected large amount of items are not returned.
Also it is setting the custom PropSet created earlier and sorting with DateTimeReceived.

PS > $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView(50,0,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
PS > $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
PS > $itemView.PropertySet=$customPropSet
PS > $itemView.OrderBy.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,[Microsoft.Exchange.WebServices.Data.SortDirection]::Ascending)

The last part is doing the actual search.
We get the results from the EWS only 50 items per query so loop though the result untill we get all the result.
The result is put into a custom object which makes the sorting and filtering in PowerShell easier.

PS > $return_object =@()

PS > do
     {
      $FindItems = $exchService.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$searchFilterCollection,$itemView)

      foreach ($eItems in $FindItems.Items){

            $props = @{ DateTimeReceived  = $eItems.DateTimeReceived
                        ArchivedDate      = $eItems.ExtendedProperties[0].Value;
                        Sender            = $eItems.Sender.Name;
                        Subject           = $eItems.Subject
                        }
      
            $return_object += New-Object -TypeName PSCustomObject -Property $props
      }

        $itemView.Offset= $itemView.Offset + $itemView.PageSize

}while ($FindItems.MoreAvailable)

At last, we have result in $return_object.
With this result, we can filter , count as you like.

PS > $return_object | where-object {(($_.ArchivedDate -gt (get-date 2014-05-07)) -and ($_.ArchivedDate -lt (get-date 2014-05-08)))} | format-table DateTimeReceived,ArchivedDate,DiffData,Sender,Subject -autosize

This is the script all in one.

Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
$exchService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::YOUR_EXCHANGE_VERSION, [System.TimeZoneInfo]::Local)

$exchService.UseDefaultCredentials = $true 
$exchService.AutodiscoverUrl("YOUR@EMAIL_ADDRESS")

$searchFilterCollection = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or)
$searchFilter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ContactSchema]::ItemClass,"IPM.Note.EnterpriseVault.Shortcut")

$searchFilterCollection.add($searchFilter1)

$prArchiveDate = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([GUID]"D0F41A15-9E91-D111-84E6-0000F877D428","Archived Date",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime)
$prArchiveDatePropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet($prArchiveDate )
$prOriginalSize = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([GUID]"D0F41A15-9E91-D111-84E6-0000F877D428","Original Size",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)
$prArchiveDatePropertySet.add($prOriginalSize)
$customPropSet = new-object Microsoft.Exchange.WebServices.Data.PropertySet($prArchiveDatePropertySet)

$customPropSet.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject)
$customPropSet.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$customPropSet.add([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Sender)

$itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView(50,0,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
$itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
$itemView.PropertySet=$customPropSet
$itemView.OrderBy.add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,[Microsoft.Exchange.WebServices.Data.SortDirection]::Ascending)

$return_object =@()

do
     {
      $FindItems = $exchService.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$searchFilterCollection,$itemView)

      foreach ($eItems in $FindItems.Items){

            $props = @{ DateTimeReceived  = $eItems.DateTimeReceived
                        ArchivedDate      = $eItems.ExtendedProperties[0].Value;
                        Sender            = $eItems.Sender.Name;
                        Subject           = $eItems.Subject;
                        Size              = $eItems.Size;
                        OriginalSize      = $eItems.ExtendedProperties[1].Value;
                        }
      
            $return_object += New-Object -TypeName PSCustomObject -Property $props
      }

        $itemView.Offset= $itemView.Offset + $itemView.PageSize

}while ($FindItems.MoreAvailable)

$return_object | where-object {(($_.ArchivedDate -gt (get-date 2014-05-07)) -and ($_.ArchivedDate -lt (get-date 2014-05-08)))} | format-table DateTimeReceived,ArchivedDate,DiffData,Sender,Subject -autosize