In this part of the how-to series I'm going to look at message classifications and how you can make use of these in your EWS scripts. Message classification was introduced in Exchange 2007 to comply with Organization policies and regulations.
Technical stuff
The extended MAPI properties and X-headers that are used for classification are documented in the following protocol document http://msdn.microsoft.com/en-us/library/ee217686%28v=EXCHG.80%29.aspx .
Lets go over then quickly
PidLidClassificationGuid is the extended property that contains the GUID of the classification that has been applied to a message. To work out these GUID's you need to use the Exchange Management Shell Get-MessageClassification cmdlet eg
So the ClassificationID = PidLidClassificationGuid
PidLidClassified : Is a Boolean property that tells if a message has been classified
PidLidClassificationDescription: The property either represents the Recipient Description on a message that is received or the Sender Description on a message that is sent.
PidLidClassification: = The displyName of the classification
Sending a Classified Message
To send a classified message with EWS you need to set these Extended Mapi properties the two Mandatory properties you need to set are the PidLidClassified and PidLidClassificationGuid Exchange should handle the other properties for you. An example of sending a classified message would look like
Finding Classified Messages
To search for classified messages within a mailbox you need to create a searchfilter based around the parameters you want to search on eg. if you have multiple classifications and you just want to limit your search to all classified messages (not just one type of classification) then you can use an exists Search filter to search for only those messages where the classification properties have been set for example
If you want to search all folders in a mailbox for message with a particular classification you can either create a searchfilter based on the ClassificationGUID eg
Or use the above code and do some client side filtering. Depending on the size of the result sets involved you may find doing client side filtering gives better performance the doing a string match on an un-indexed property. Here is a sample for searching all folders in a mailbox for messages with a particular Message classification GUID and doing client side filter on that match and then producing a CSV report of the location of these messages.
Technical stuff
The extended MAPI properties and X-headers that are used for classification are documented in the following protocol document http://msdn.microsoft.com/en-us/library/ee217686%28v=EXCHG.80%29.aspx .
Lets go over then quickly
PidLidClassificationGuid is the extended property that contains the GUID of the classification that has been applied to a message. To work out these GUID's you need to use the Exchange Management Shell Get-MessageClassification cmdlet eg
So the ClassificationID = PidLidClassificationGuid
PidLidClassified : Is a Boolean property that tells if a message has been classified
PidLidClassificationDescription: The property either represents the Recipient Description on a message that is received or the Sender Description on a message that is sent.
PidLidClassification: = The displyName of the classification
Sending a Classified Message
To send a classified message with EWS you need to set these Extended Mapi properties the two Mandatory properties you need to set are the PidLidClassified and PidLidClassificationGuid Exchange should handle the other properties for you. An example of sending a classified message would look like
- $message = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage($service)
- $message.Subject = "Test"
- $message.Body = "Test 123"
- $message.ToRecipients.Add("user@domain.com")
- $PidLidClassificationGuid = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common, 34232,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
- $message.SetExtendedProperty($PidLidClassificationGuid, "e67e794b-f6d1-4c8f-9f63-1118d21dafa6");
- $PidLidClassified = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,34229,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Boolean);
- $message.SetExtendedProperty($PidLidClassified, $true);
- $message.Send.Invoke()
Finding Classified Messages
To search for classified messages within a mailbox you need to create a searchfilter based around the parameters you want to search on eg. if you have multiple classifications and you just want to limit your search to all classified messages (not just one type of classification) then you can use an exists Search filter to search for only those messages where the classification properties have been set for example
- $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
- $Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
- $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
- $PidLidClassificationGuid = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common, 34232,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
- $PidLidClassified = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,0x85B6,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
- $psPropertySet.Add($PidLidClassificationGuid)
- $psPropertySet.Add($PidLidClassified)
- $ItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists($PidLidClassificationGuid)
- $ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
- $ivItemView.PropertySet = $psPropertySet
- do{
- $fiResults = $Inbox.findItems($ItemSearchFilter,$ivItemView)
- foreach($Item in $fiResults.Items){
- $classificationGUID = $null
- [Void]$Item.TryGetProperty($PidLidClassificationGuid,[ref]$classificationGUID)
- $Classification = $null
- [Void]$Item.TryGetProperty($PidLidClassified,[ref]$Classification)
- "Subject : " + $Item.Subject
- "Received : " + $Item.DateTimeReceived
- "Classification : " + $Classification
- "ClassificationGUID : " + $classificationGUID
- }
- $ivItemView.Offset += $fiResults.Items.Count
- }while($fiResults.MoreAvailable -eq $true)
If you want to search all folders in a mailbox for message with a particular classification you can either create a searchfilter based on the ClassificationGUID eg
- $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PidLidClassificationGuid, "e67e794b-f6d1-4c8f-9f63-1118d21dafa6")
- $RptObjColl = @()
- $MailboxName = "user@domain.com"
- $guid = "e67e794b-f6d1-4c8f-9f63-1118d21dafa6"
- ## Load Managed API dll
- Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
- ## Set Exchange Version
- $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1
- ## Create Exchange Service Object
- $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
- ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
- #Credentials Option 1 using UPN for the windows Account
- $creds = New-Object System.Net.NetworkCredential("glen@domain.com","passwd")
- $service.Credentials = $creds
- #Credentials Option 2
- #service.UseDefaultCredentials = $true
- ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
- [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
- ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
- #CAS URL Option 1 Autodiscover
- $service.AutodiscoverUrl($MailboxName,{$true})
- "Using CAS Server : " + $Service.url
- #CAS URL Option 2 Hardcoded
- #$uri=[system.URI] "https://casservername/ews/exchange.asmx"
- #$service.Url = $uri
- ## Optional section for Exchange Impersonation
- #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
- #Define Function to convert String to FolderPath
- function ConvertToString($ipInputString){
- $Val1Text = ""
- for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){
- $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))
- $clInt++
- }
- return $Val1Text
- }
- $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
- $MsgRoot = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
- $fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
- #Deep Transval will ensure all folders in the search path are returned
- $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;
- $ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
- $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
- $PidLidClassificationGuid = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common, 34232,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
- $PidLidClassified = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,34229,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Boolean);
- $psPropertySet.Add($PidLidClassificationGuid)
- $psPropertySet.Add($PidLidClassified)
- $FPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
- $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
- $FPropertySet.add($PR_Folder_Path)
- $fvFolderView.PropertySet = $FPropertySet
- $fiResult = $null
- #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox
- $rptHash = @{}
- $ItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists($PidLidClassificationGuid)
- do {
- $ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
- $ivItemView.PropertySet = $psPropertySet
- $fiResult = $Service.FindFolders($folderid,$sfSearchFilter,$fvFolderView)
- foreach($ffFolder in $fiResult.Folders){
- "Processing Folder : " + $ffFolder.displayName
- $foldpathval = $null
- #Try to get the FolderPath Value and then covert it to a usable String
- if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))
- {
- $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)
- $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }
- $hexString = $hexArr -join ''
- $hexString = $hexString.Replace("FEFF", "5C00")
- $fpath = ConvertToString($hexString)
- }
- if($ffFolder.TotalCount -gt 0){
- $fiResults = $null
- $updateColl = @()
- do{
- $fiResults = $ffFolder.findItems($ItemSearchFilter,$ivItemView)
- $rptobj = "" | select FolderPath,DateTimeRecieved,Subject,Size,MessageGUID
- foreach($Item in $fiResults.Items){
- $messageGuid = $null
- if($Item.TryGetProperty($PidLidClassificationGuid,[ref]$messageGuid)){
- if($messageGuid -eq $guid){
- $rptobj.MessageGUID = $messageGuid
- $rptobj.FolderPath = $fpath
- $rptobj.DateTimeRecieved = $Item.DateTimeReceived
- $rptobj.Subject = $Item.Subject
- $rptobj.Size = $Item.Size
- $RptObjColl += $rptobj
- }
- }
- }
- $ivItemView.Offset += $fiResults.Items.Count
- }while($fiResults.MoreAvailable -eq $true)
- }
- }
- $fvFolderView.Offset += $fiResult.Folders.Count
- }while($fiResult.MoreAvailable -eq $true)
- $RptObjColl | Export-Csv -NoTypeInformation c:\Temp\classificationReport.csv