Quantcast
Channel: Glen's Exchange and Office 365 Dev Blog
Viewing all 241 articles
Browse latest View live

Sending a VoiceMail using EWS and Powershell

$
0
0
If you want to send a VoiceMail via EWS and have the UM controls appear in Outlook and OWA so it appears the same as it was generated from Exchange UM you need to set a number of different extended properties on an Email Message when you send it. The full list of properties is documented in the following Exchange Protocol document .

While some of these properties are optional the most important is to the set the PidTagVoiceMessageAttachmentOrder  http://msdn.microsoft.com/en-us/library/ee202885%28v=exchg.80%29.aspx property which needs to be set to the name of the attachment. You also need to make sure you set the ItemClass to IPM.Note.Microsoft.Voicemail.UM.CA .

The following is a sample of sending a mp3 voicemail as a message with powershell, I've put a download of this script here .

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. #MessageProperties  
  6.   
  7. $VoiceMailSuject = "Voice Mail from .."  
  8. $Mp3FileName = "c:\temp\vmMail.mp3"  
  9. $duration = 30  
  10. $voiceMailFrom = "FirstName LastName"  
  11. $callerId = "1234"  
  12. $jobTitle = "Tech"  
  13. $Company = "CompanyName"  
  14. $workNumber = "11111-11111"  
  15. $emailAddress = "test@domain.com"  
  16. $SipAddress = "sip:test@domain.com"  
  17. $ToAddress = "user@domain.com"  
  18.   
  19. $fileInfo = Get-Item $Mp3FileName  
  20.   
  21. $BodyHtml = "<html><head><META HTTP-EQUIV=`"Content-Type`" CONTENT=`"text/html; charset=us-ascii`">"  
  22. $BodyHtml += "<style type=`"text/css`"> a:link { color: #3399ff; } a:visited { color: #3366cc; } a:active { color: #ff9900; } </style>"  
  23. $BodyHtml += "</head><body><style type=`"text/css`"> a:link { color: #3399ff; } a:visited { color: #3366cc; } a:active { color: #ff9900; } </style>"  
  24. $BodyHtml += "<div style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`"><div id=`"UM-call-info`" lang=`"en`">"  
  25. $BodyHtml += "<div style=`"font-family: Arial; font-size: 10pt; color:#000066; font-weight: bold;`">You received a voice mail from " + $voiceMailFrom + " at " + $callerId + "</div>"  
  26. $BodyHtml += "<br><table border=`"0`" width=`"100%`"><tr><td width=`"12px`"></td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">"  
  27. $BodyHtml += "Caller-Id:</td><td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  28. $BodyHtml += "<a style=`"color: #3399ff; `" dir=`"ltr`" href=`"tel:" + $callerId + "`">" + $callerId + "</a></td></tr><tr><td width=`"12px`"></td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">"  
  29. $BodyHtml += "Job Title:</td><td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  30. $BodyHtml += $jobTitle + "</td></tr><tr><td width=`"12px`"></td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">"  
  31. $BodyHtml += "Company:</td><td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  32. $BodyHtml += $Company + "</td></tr><tr><td width=`"12px`"></td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">"  
  33. $BodyHtml += "Work:</td><td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  34. $BodyHtml += "<a style=`"color: #3399ff; `" dir=`"ltr`" href=`"tel:&#43;" + $workNumber + "`">&#43;" + $workNumber + "</a></td></tr><tr><td width=`"12px`"></td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">"  
  35. $BodyHtml += "E-mail:</td><td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  36. $BodyHtml += "<a style=`"color: #3399ff; `" href=`"mailto:" + $emailAddress + "`">" + $voiceMailFrom + "</a></td></tr><tr><td width=`"12px`">"  
  37. $BodyHtml += "</td><td width=`"28%`" nowrap=`"`" style=`"font-family: Tahoma; color: #686a6b; font-size:10pt;border-width: 0in;`">IM Address:</td>"  
  38. $BodyHtml += "<td width=`"72%`" style=`"font-family: Tahoma; background-color: #ffffff; color: #000000; font-size:10pt;`">"  
  39. $BodyHtml += "<a style=`"color: #3399ff; `" href=`"" + $SipAddress + "`">" + $emailAddress + "</a></td></tr></table></div></div></body></html>"  
  40.   
  41.   
  42.   
  43. ## Load Managed API dll    
  44. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  45.     
  46. ## Set Exchange Version    
  47. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  48.     
  49. ## Create Exchange Service Object    
  50. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  51.     
  52. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  53.     
  54. #Credentials Option 1 using UPN for the windows Account    
  55. $psCred = Get-Credential    
  56. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  57. $service.Credentials = $creds        
  58.     
  59. #Credentials Option 2    
  60. #service.UseDefaultCredentials = $true    
  61.     
  62. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  63.     
  64. ## Code From http://poshcode.org/624  
  65. ## Create a compilation environment  
  66. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  67. $Compiler=$Provider.CreateCompiler()  
  68. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  69. $Params.GenerateExecutable=$False  
  70. $Params.GenerateInMemory=$True  
  71. $Params.IncludeDebugInformation=$False  
  72. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  73.   
  74. $TASource=@' 
  75.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  76.     public class TrustAll : System.Net.ICertificatePolicy { 
  77.       public TrustAll() {  
  78.       } 
  79.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  80.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  81.         System.Net.WebRequest req, int problem) { 
  82.         return true; 
  83.       } 
  84.     } 
  85.   } 
  86. '@   
  87. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  88. $TAAssembly=$TAResults.CompiledAssembly  
  89.   
  90. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  91. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  92. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  93.   
  94. ## end code from http://poshcode.org/624  
  95.     
  96. ## 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    
  97.     
  98. #CAS URL Option 1 Autodiscover    
  99. $service.AutodiscoverUrl($MailboxName,{$true})    
  100. "Using CAS Server : " + $Service.url     
  101.      
  102. #CAS URL Option 2 Hardcoded    
  103.     
  104. #$uri=[system.URI] "https://192.168.0.6/ews/exchange.asmx"    
  105. #$service.Url = $uri      
  106.     
  107. ## Optional section for Exchange Impersonation    
  108.     
  109. #Extended Prop Definition  
  110.   
  111. $PidTagVoiceMessageAttachmentOrder = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6805, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  112. $PstnCallbackTelephoneNumberVal = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::UnifiedMessaging,"PstnCallbackTelephoneNumber", [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  113. $PidTagSIPAddress = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x5FE5, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  114. $PidTagCallIdVal = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6806, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  115. $PidTagSenderTelephoneNumber = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6802, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  116. $PidTagVoiceMessageDuration =  new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6801, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  117. $PidTagVoiceMessageSenderName = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6803, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  118.    
  119. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  120. $EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service    
  121. $EmailMessage.Subject = $VoiceMailSuject  
  122. #Add Recipients      
  123. $EmailMessage.ToRecipients.Add($ToAddress)    
  124. $EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody    
  125. $EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML    
  126. $EmailMessage.Body.Text = $BodyHtml  
  127. $EmailMessage.ItemClass = "IPM.Note.Microsoft.Voicemail.UM.CA"  
  128. $vmAttachment = $EmailMessage.Attachments.AddFileAttachment($Mp3FileName)  
  129. $vmAttachment.ContentType = "audio/mp3";  
  130. $EmailMessage.SetExtendedProperty($PidTagVoiceMessageAttachmentOrder,$fileInfo.Name)  
  131. $EmailMessage.SetExtendedProperty($PstnCallbackTelephoneNumberVal,$workNumber)  
  132. $EmailMessage.SetExtendedProperty($PidTagSIPAddress,$SipAddress)  
  133. $EmailMessage.SetExtendedProperty($PidTagCallIdVal,$callerId)  
  134. $EmailMessage.SetExtendedProperty($PidTagSenderTelephoneNumber,$workNumber)  
  135. $EmailMessage.SetExtendedProperty($PidTagVoiceMessageDuration,$duration)  
  136. $EmailMessage.SetExtendedProperty($PidTagVoiceMessageSenderName,$voiceMailFrom)  
  137. $EmailMessage.SendAndSaveCopy()    





Public Folder EWS How-To rollup Part 1

$
0
0
As Public Folders are now new again in Exchange 2013, I thought I'd put together a post that covered how you can go about accessing and doing certain things with Public folders via EWS and Powershell. Although the underlying way in which Public Folders are now delivered in Exchange 2013 is vastly different from previous version of Exchange the way in which you access them Programmatically in EWS hasn't changed since Exchange 2007. So if your worried about your 2007 EWS code not working against 2013 don't only some minor changes maybe needed. (although if you have WebDAV code your going to need to rewrite this).

Choosing the Right API Method for what you want to do

The big thing when your considering creating a Public Folder application is to understand and choose the correct API for what you want to do. Eg if you want to write an application that is going to manage Public Folders eg Size/creation/deletion/Mail-Enable and folders permissions then EWS or MAPI are not the correct API to use as these are client API's. For any admin style applications you should be using the Exchange Management Shell cmdlets which give you a fully functional entry point to do administration tasks and also more importantly the ability to assert Delegated Exchange Admin Rights. Which means you can manage the folder without the need to have explicit client permissions on the folders. When you using a client API like EWS or MAPI you will only be able to perform client based actions where you have rights granted on the DACL of that particular folder (or inherited client permissions from the Parent). Unlike WebDAV in 2003,2007 there is no Virtual Admin root with EWS where Delegated Admin rights can be asserted so the EMS cmdlets via Remote Powershell is the best solution for Admin Apps.

EWS Public Folder operations

There are no dedicated Public Folder operations in EWS, so everything you do is with a Subset of the existing Mailbox operations. There is a full list of EWS Operations that work with Public Folder and notes around the restriction in http://msdn.microsoft.com/en-us/library/exchange/jj945067%28v=exchg.150%29.aspx. One of the big improvements in 2013 is now the ability to use Content Indexes to query Public Folder content which includes the ability to scan Attachment content where an IFilter is available for that attachment type.

Getting the Public Folder Hierarchy

When you want to return the full folder hierarchy of  a Mailbox with EWS you use a FindFolder operation where you set the FolderView to tell the server to perform a deep traversal which means that all folders and subfolder will be queried. If you had more then 1000 folders in a Mailbox you would need to make multiple request to get all the folders back paged 1000 at a time to ensure you stayed under the throttling limits. With Public Folders because Deep Traversals aren't supported in a FindFolder operation it presents a challenge to getting the Full folder hierarchy.Those people who are migrating from a MAPI application where they have used "GetHierarchyTable" in MAPI, may find EWS a little irritating as there is no easy way of quickly returning a full folder hierarchy in a short time amount of time like MAPI. However its not all bad news, one example of how to get the hierarchy is the method used in OWA in 2013 which makes use of multiple FindFolder operations to show the Public Folder tree. OWA just does an on demand query when the user clicks each client node which means there is a slight pause while the operation completes. It just means living with a slightly less responsive UI.

In Powershell if you want to get the Full Folder hierarchy you would need to do multiple shallow traversals like the following example

  1. function ConvertToString($ipInputString){    
  2.     $Val1Text = ""    
  3.     for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  4.             $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  5.             $clInt++    
  6.     }    
  7.     return $Val1Text    
  8. }   
  9.   
  10. function GetPublicFolders{  
  11.     param (  
  12.             $RootFolderId = "$( throw 'FolderId is a mandatory Parameter' )"  
  13.           )  
  14.     process{  
  15.         $RootFolderId  
  16.         $folderCollection = @()  
  17.         #Define Extended properties    
  18.         $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  19.         #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  20.         $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  21.         #Deep Transval will ensure all folders in the search path are returned    
  22.         $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Shallow;    
  23.         $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  24.         $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  25.         #Add Properties to the  Property Set    
  26.         $psPropertySet.Add($PR_Folder_Path);    
  27.         $fvFolderView.PropertySet = $psPropertySet;    
  28.         #The Search filter will exclude any Search Folders    
  29.         $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  30.         $fiResult = $null    
  31.         do {    
  32.             $fiResult = $Service.FindFolders($RootFolderId,$sfSearchFilter,$fvFolderView)    
  33.             foreach($ffFolder in $fiResult.Folders){    
  34.                 $foldpathval = $null    
  35.                 $folderCollection += $ffFolder  
  36.                 #Try to get the FolderPath Value and then covert it to a usable String     
  37.                 if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  38.                 {    
  39.                     $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  40.                     $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  41.                     $hexString = $hexArr -join ''    
  42.                     $hexString = $hexString.Replace("FEFF""5C00")    
  43.                     $fpath = ConvertToString($hexString)    
  44.                 }    
  45.                 "FolderPath : " + $fpath    
  46.                 if($ffFolder.ChildFolderCount -gt 0){  
  47.                     $Childfolders = GetPublicFolders -RootFolderId $ffFolder.Id  
  48.                     foreach($Childfolder in $Childfolders){  
  49.                         $folderCollection += $Childfolder  
  50.                     }  
  51.                 }  
  52.             }  
  53.             $fvFolderView.Offset += $fiResult.Folders.Count  
  54.         }while($fiResult.MoreAvailable -eq $true)    
  55.         return $folderCollection  
  56.     }  
  57. }  
  58.   
  59. $pfRoot = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::PublicFoldersRoot)    
  60. $folderHi = GetPublicFolders -RootFolderId $pfRoot  
  61. #Example Use
  62. foreach($fld in $folderHi){  
  63.     $fld.DisplayName  
  64. }  
Binding to a specific Public folder

If you want to bind to a specific public folder given a path as \FolderLevel1\Level2\Level3\Target you need to know the particular folders EWSId. There are a few ways of getting the FolderId if your using Outlook you could take the HexEntryId can get from OWA and convert that into an EWSId eg the following is an example of using the AlternatePublicFolderId to covert the HexEntryId to an EWSId

  1. function ConvertId{      
  2.     param (  
  3.             $HexId = "$( throw 'HexId is a mandatory Parameter' )"  
  4.           )  
  5.     process{  
  6.         $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternatePublicFolderId       
  7.         $aiItem.FolderId = $HexId     
  8.         $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId        
  9.         $convertedId = $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EwsId)   
  10.         return $convertedId.UniqueId  
  11.     }  
  12. }  
The other method you can use is to search for your target Folder using multi find folder operations and by splitting the path you want to search for eg.

  1. function FolderIdFromPath{  
  2.     param (  
  3.             $FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"  
  4.           )  
  5.     process{  
  6.         ## Find and Bind to Folder based on Path    
  7.         #Define the path to search should be seperated with \    
  8.         #Bind to the MSGFolder Root    
  9.         $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::PublicFoldersRoot)     
  10.         $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)    
  11.         #Split the Search path into an array    
  12.         $fldArray = $FolderPath.Split("\")  
  13.          #Loop through the Split Array and do a Search for each level of folder  
  14.         for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {  
  15.             #Perform search based on the displayname of each folder level  
  16.             $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)  
  17.             $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])  
  18.             $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)  
  19.             if ($findFolderResults.TotalCount -gt 0){  
  20.                 foreach($folder in $findFolderResults.Folders){  
  21.                     $tfTargetFolder = $folder                 
  22.                 }  
  23.             }  
  24.             else{  
  25.                 "Error Folder Not Found"   
  26.                 $tfTargetFolder = $null   
  27.                 break   
  28.             }      
  29.         }   
  30.         if($tfTargetFolder -ne $null){ 
  31.             return $tfTargetFolder.Id.UniqueId.ToString() 
  32.         } 
  33.     } 
  34. } 
  35.  
  36. #Example use 
  37. $fldId = FolderIdFromPath -FolderPath "\firstlevel\secondlevel\target"  
  38. $SubFolderId =  new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId)  
  39. $PubFolder  = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId)  
  40. $PubFolder  
Once you have bound to the Folder you can then perform any of the normal Item operations which I covered in the following post 

In Part 2 I'll go into some more depth some of the harder aspects of Public Folders.

Exchange Attachment statistics reporting with EWS and Powershell

$
0
0
The following script is a combination of a couple of other scripts I've posted in the past, what it does is compiles statistics of the messages with and without attachments in a Mailbox and then produces a report. To do this in EWS you need to look at every Item in the mailbox,  if the item has attachments it determines how large these attachments are and keeps a running tab of the largest attachment in the mailbox. In Exchange there are a number of different attachment types but EWS divides them into two distinct types, File Attachments and ItemAttachment which are basically attached Exchange Items. This script compile statistics around these two distinct types but doesn't process down to the embeeded attachment level. The results of all the requests are added together to produce a final report that reflects the attachment statistics for the entire mailbox. A picture is worth a thousand words so it produces a report like


 To get the information about the Items in the Mailbox the FindItem Operation is used, however as FindItem doesn't return detailed information about attachments the LoadPropertiesForItems method is used which does a batch GetItem request on a collection of results. One thing to note is the AttachmentSize information isn't returned by EWS in Exchange 2007 so this script will only work for Exchange 2010 up.

This script will prompt for what security credentials you want to use and these creds need full access to the Mailboxes your scanning (an alternative would be to use Impersonation but that would require the script to be changed).

You need to feed the script with a CSV file like

smtpaddress
user1@domain.com
user2@domain.com

I've put a download of this script here  the script itself looks like.

  1. $Script:rptCollection = @()    
  2. ## Load Managed API dll    
  3. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  4.     
  5. ## Set Exchange Version    
  6. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  7.     
  8. ## Create Exchange Service Object    
  9. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  10.     
  11. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  12.     
  13. #Credentials Option 1 using UPN for the windows Account    
  14. $psCred = Get-Credential    
  15. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  16. $service.Credentials = $creds        
  17.     
  18. #Credentials Option 2    
  19. #service.UseDefaultCredentials = $true    
  20.     
  21. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  22.     
  23. ## Code From http://poshcode.org/624  
  24. ## Create a compilation environment  
  25. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  26. $Compiler=$Provider.CreateCompiler()  
  27. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  28. $Params.GenerateExecutable=$False  
  29. $Params.GenerateInMemory=$True  
  30. $Params.IncludeDebugInformation=$False  
  31. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  32.   
  33. $TASource=@' 
  34.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  35.     public class TrustAll : System.Net.ICertificatePolicy { 
  36.       public TrustAll() {  
  37.       } 
  38.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  39.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  40.         System.Net.WebRequest req, int problem) { 
  41.         return true; 
  42.       } 
  43.     } 
  44.   } 
  45. '@   
  46. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  47. $TAAssembly=$TAResults.CompiledAssembly  
  48.   
  49. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  50. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  51. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  52.   
  53. ## end code from http://poshcode.org/624  
  54.     
  55. ## 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    
  56.     
  57. #CAS URL Option 1 Autodiscover    
  58. #$service.AutodiscoverUrl($MailboxName,{$true})    
  59. #"Using CAS Server : " + $Service.url     
  60.      
  61. #CAS URL Option 2 Hardcoded    
  62.     
  63. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  64. #$service.Url = $uri      
  65.     
  66. ## Optional section for Exchange Impersonation    
  67.     
  68. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  69.   
  70.   
  71. function Process-Mailbox{  
  72.     param (  
  73.             $SmtpAddress = "$( throw 'SMTPAddress is a mandatory Parameter' )"  
  74.           )  
  75.     process{  
  76.     $rptObj = "" | select MailboxName,TotalItem,TotalItemSize,TotalItemsNoAttach,TotalItemsNoAttachSize,TotalItemsAttach,TotalItemsAttachSize,TotalFileAttachments,TotalFileAttachmentsSize,TotalItemAttachments,TotalItemAttachmentsSize,LargestAttachmentSize,LargestAttachmentName  
  77.     $rptObj.MailboxName = $SmtpAddress  
  78.     $rptObj.TotalItem = 0  
  79.     $rptObj.TotalItemSize = [Int64]0  
  80.     $rptObj.TotalItemsNoAttach = 0  
  81.     $rptObj.TotalItemsNoAttachSize = [Int64]0  
  82.     $rptObj.TotalItemsAttach = 0  
  83.     $rptObj.TotalItemsAttachSize = [Int64]0  
  84.     $rptObj.TotalFileAttachments = 0  
  85.     $rptObj.TotalFileAttachmentsSize  = [Int64]0  
  86.     $rptObj.TotalItemAttachments = 0  
  87.     $rptObj.TotalItemAttachmentsSize  = [Int64]0  
  88.     $rptObj.LargestAttachmentSize = [Int64]0  
  89.     $rptObj.LargestAttachmentName = ""  
  90.     "Processing Mailbox : " + $SmtpAddress  
  91.       
  92.     #check Anchor header for Exchange 2013/Office365  
  93.     if($service.HttpHeaders.ContainsKey("X-AnchorMailbox")){  
  94.         $service.HttpHeaders["X-AnchorMailbox"] = $SmtpAddress  
  95.     }else{  
  96.         $service.HttpHeaders.Add("X-AnchorMailbox"$SmtpAddress);  
  97.     }  
  98.     "AnchorMailbox : " + $service.HttpHeaders["X-AnchorMailbox"]  
  99.     #Define ItemView to retrive just 1000 Items      
  100.     $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  101.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  102.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)  
  103.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)  
  104.     $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)  
  105.     $ivItemView.PropertySet = $psPropset  
  106.     $TotalSize = 0  
  107.     $TotalItemCount = 0  
  108.   
  109.   
  110.     #Define Function to convert String to FolderPath    
  111.     function ConvertToString($ipInputString){    
  112.         $Val1Text = ""    
  113.         for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  114.                 $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  115.                 $clInt++    
  116.         }    
  117.         return $Val1Text    
  118.     }   
  119.   
  120.     #Define Extended properties    
  121.     $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  122.     $folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SmtpAddress)    
  123.     #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  124.     $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  125.     #Deep Transval will ensure all folders in the search path are returned    
  126.     $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;    
  127.     $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  128.     $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  129.     #Add Properties to the  Property Set    
  130.     $psPropertySet.Add($PR_Folder_Path);    
  131.     $fvFolderView.PropertySet = $psPropertySet;    
  132.     #The Search filter will exclude any Search Folders    
  133.     $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  134.     $fiResult = $null    
  135.     #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox    
  136.     do {    
  137.         $fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)    
  138.         foreach($ffFolder in $fiResult.Folders){    
  139.             $foldpathval = $null    
  140.             #Try to get the FolderPath Value and then covert it to a usable String     
  141.             if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  142.             {    
  143.                 $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  144.                 $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  145.                 $hexString = $hexArr -join ''    
  146.                 $hexString = $hexString.Replace("FEFF""5C00")    
  147.                 $fpath = ConvertToString($hexString)    
  148.             }    
  149.               
  150.             $totalItemCnt = 1  
  151.             if($ffFolder.TotalCount -ne $null){  
  152.                 $totalItemCnt = $ffFolder.TotalCount  
  153.                 "Processing FolderPath : " + $fpath  + " Item Count " + $totalItemCnt  
  154.             }  
  155.             else{  
  156.                 "Processing FolderPath : " + $fpath  
  157.             }  
  158.             if($totalItemCnt -gt 0){  
  159.                 #Define ItemView to retrive just 1000 Items      
  160.                 $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  161.                 $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  162.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Size)  
  163.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)  
  164.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeCreated)  
  165.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::Attachments)  
  166.                 $psPropset.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::HasAttachments)  
  167.                 $fipsPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)    
  168.                 $ivItemView.PropertySet = $fipsPropset        
  169.                 $fiItems = $null      
  170.                 do{      
  171.                     $fiItems = $service.FindItems($ffFolder.Id,$ivItemView)   
  172.                     if($fiItems.Items.Count -gt 0){  
  173.                         [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)   
  174.                         "processing : " + $fiItems.Items.Count + " Items"  
  175.                         foreach($Item in $fiItems.Items){  
  176.                             $rptObj.TotalItem +=1  
  177.                             $rptObj.TotalItemSize += [Int64]$Item.Size  
  178.                             if($Item.Attachments.Count -gt 0){  
  179.                                 $rptObj.TotalItemsAttach +=1  
  180.                                 $rptObj.TotalItemsAttachSize += [Int64]$Item.Size  
  181.                                 foreach($Attachment in $Item.Attachments){                            
  182.                                     if($Attachment -is [Microsoft.Exchange.WebServices.Data.FileAttachment]){  
  183.                                         $rptObj.TotalFileAttachments +=1  
  184.                                         $rptObj.TotalFileAttachmentsSize += $Attachment.Size  
  185.                                         $attachSize = [Math]::Round($Attachment.Size/1MB,2)  
  186.                                         if($attachSize -gt $rptobj.LargestAttachmentSize){  
  187.                                             $rptobj.LargestAttachmentSize = $attachSize  
  188.                                             $rptobj.LargestAttachmentName = $Attachment.Name  
  189.                                         }  
  190.                                     }  
  191.                                     else{  
  192.                                         $rptObj.TotalItemAttachments +=1  
  193.                                         $rptObj.TotalItemAttachmentsSize += $Attachment.Size  
  194.                                     }  
  195.                                 }  
  196.                             }  
  197.                             else{  
  198.                                 $rptObj.TotalItemsNoAttach +=1  
  199.                                 $rptObj.TotalItemsNoAttachSize += [Int64]$Item.Size  
  200.                             }  
  201.                         }  
  202.                     }      
  203.                     $ivItemView.Offset += $fiItems.Items.Count      
  204.                 }while($fiItems.MoreAvailable -eq $true)  
  205.             }  
  206.         }   
  207.         $fvFolderView.Offset += $fiResult.Folders.Count  
  208.     }while($fiResult.MoreAvailable -eq $true)  
  209.     #convert Sizes to MB  
  210.   
  211.     if($rptObj.TotalItemSize -ne 0){  
  212.         $rptObj.TotalItemSize = [Math]::Round($rptObj.TotalItemSize/1MB)  
  213.     }  
  214.     if($rptObj.TotalItemsNoAttachSize -ne 0){  
  215.         $rptObj.TotalItemsNoAttachSize = [Math]::Round($rptObj.TotalItemsNoAttachSize/1MB)  
  216.     }  
  217.     if($rptObj.TotalItemsAttachSize -ne 0){  
  218.         $rptObj.TotalItemsAttachSize = [Math]::Round($rptObj.TotalItemsAttachSize/1MB)  
  219.     }  
  220.     if($rptObj.TotalFileAttachmentsSize -ne 0){  
  221.         $rptObj.TotalFileAttachmentsSize = [Math]::Round($rptObj.TotalFileAttachmentsSize/1MB)  
  222.     }  
  223.     if($rptObj.TotalItemAttachmentsSize -ne 0){  
  224.         $rptObj.TotalItemAttachmentsSize = [Math]::Round($rptObj.TotalItemAttachmentsSize/1MB)  
  225.     }  
  226.     $Script:rptCollection += $rptObj  
  227.     }  
  228. }  
  229.   
  230. Import-Csv -Path $args[0] | ForEach-Object{  
  231.     if($service.url -eq $null){  
  232.         $service.AutodiscoverUrl($_.SmtpAddress,{$true})   
  233.         "Using CAS Server : " + $Service.url   
  234.     }  
  235.     Try{  
  236.         Process-Mailbox -SmtpAddress $_.SmtpAddress  
  237.     }  
  238.     catch{  
  239.         LogWrite("Error processing Mailbox : " + $_.SmtpAddress + $_.Exception.Message.ToString())  
  240.     }  
  241. }  
  242. $Script:rptCollection | Export-Csv -NoTypeInformation -Path c:\temp\mbAttachReport.csv  




Junk Email SCL report using EWS and Powershell

$
0
0
Dealing with Junk Email is kind of like doing the dishes, it's dirty work but somebodies got to do it. While Spam filters are improving we know by the constant updates these filters require its an invisible war that's always going on. Also software being what it is, things don't always go to plan like this http://support.microsoft.com/kb/2885002/en-us

So it can be good to check in on what's happening in your User's JunkEmail folders from time to time and EWS is great for doing this so.

The following EWS script scans all the messages in the JunkEmail folder of a Mailbox (or Mailboxes) and looks at the SCL value of each of the Messages which is held in the

PidTagContentFilterSpamConfidenceLevel property

And also looks at the

PidLidSpamOriginalFolder property which contains the original folder a Message was moved from, which only gets set when the Outlook Junk Email Filter takes an action on a message vs the Server Side action (where this properties doesn't get set)

This script produces a report that shows the number of message for each SCL levels, total number of messages in the JunkEmail folder and the number of Messages move just by just Outlook Filter eg.


To run the script you need to feed it with a CSV with the mailbox you want to run it against something like

SmtpAddress
user@domain.com
user2@domain.com

Then use .\sclReport.pst .\users.csv

I've put a download of the script here

The script looks like

  1. $Script:rptCollection = @()    
  2.   
  3. ## Load Managed API dll    
  4. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  5.     
  6. ## Set Exchange Version    
  7. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  8.     
  9. ## Create Exchange Service Object    
  10. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  11.     
  12. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  13.     
  14. #Credentials Option 1 using UPN for the windows Account    
  15. $psCred = Get-Credential    
  16. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  17. $service.Credentials = $creds        
  18.     
  19. #Credentials Option 2    
  20. #service.UseDefaultCredentials = $true    
  21.     
  22. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  23.     
  24. ## Code From http://poshcode.org/624  
  25. ## Create a compilation environment  
  26. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  27. $Compiler=$Provider.CreateCompiler()  
  28. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  29. $Params.GenerateExecutable=$False  
  30. $Params.GenerateInMemory=$True  
  31. $Params.IncludeDebugInformation=$False  
  32. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  33.   
  34. $TASource=@' 
  35.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  36.     public class TrustAll : System.Net.ICertificatePolicy { 
  37.       public TrustAll() {  
  38.       } 
  39.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  40.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  41.         System.Net.WebRequest req, int problem) { 
  42.         return true; 
  43.       } 
  44.     } 
  45.   } 
  46. '@   
  47. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  48. $TAAssembly=$TAResults.CompiledAssembly  
  49.   
  50. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  51. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  52. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  53.   
  54. ## end code from http://poshcode.org/624  
  55.     
  56. ## 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    
  57.     
  58. #CAS URL Option 2 Hardcoded    
  59.     
  60. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  61. #$service.Url = $uri      
  62.     
  63. ## Optional section for Exchange Impersonation    
  64.     
  65. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $SmtpAddress)   
  66.   
  67. function Process-Mailbox{  
  68.     param (  
  69.             $SmtpAddress = "$( throw 'SMTPAddress is a mandatory Parameter' )"  
  70.           )  
  71.     process{  
  72.         Write-Host ("Processing Mailbox : " + $SmtpAddress)  
  73.         $rptObj = "" | select MailboxName,TotalItems,MovedByOutlook,NoSCL,SCLneg1,SCL0,SCL1,SCL2,SCL3,SCL4,SCL5,SCL6,SCL7,SCL8,SCL9  
  74.         $rptObj.MovedByOutlook = 0  
  75.         $rptObj.NoSCL = 0  
  76.         $rptObj.SCLneg1 = 0  
  77.         $rptObj.SCL0 = 0  
  78.         $rptObj.SCL1 = 0  
  79.         $rptObj.SCL2 = 0  
  80.         $rptObj.SCL3 = 0  
  81.         $rptObj.SCL4 = 0  
  82.         $rptObj.SCL5 = 0  
  83.         $rptObj.SCL6 = 0  
  84.         $rptObj.SCL7 = 0  
  85.         $rptObj.SCL8 = 0  
  86.         $rptObj.SCL9 = 0  
  87.         # Bind to the Junk Email Folder  
  88.         $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::JunkEmail,$SmtpAddress)     
  89.         $JunkEmail = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  90.         $rptObj.MailboxName = $SmtpAddress  
  91.         $rptObj.TotalItems = $JunkEmail.TotalCount  
  92.         $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)  
  93.         $PidTagContentFilterSpamConfidenceLevel = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x4076, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  94.         $PidLidSpamOriginalFolder = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,0x859C, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  95.         $psPropset.Add($PidTagContentFilterSpamConfidenceLevel)  
  96.         $psPropset.Add($PidLidSpamOriginalFolder)  
  97.         #Define ItemView to retrive just 1000 Items      
  98.         $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  99.         $ivItemView.PropertySet = $psPropset  
  100.         $fiItems = $null      
  101.         do{      
  102.             $fiItems = $service.FindItems($JunkEmail.Id,$ivItemView)      
  103.             #[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  104.             foreach($Item in $fiItems.Items){  
  105.                 $OrigFldVal = $null  
  106.                 if($Item.TryGetProperty($PidLidSpamOriginalFolder,[ref]$OrigFldVal)){  
  107.                     $rptObj.MovedByOutlook +=1  
  108.                 }  
  109.                 $SCLVal = $null;  
  110.                 if($Item.TryGetProperty($PidTagContentFilterSpamConfidenceLevel,[ref]$SCLVal)){  
  111.                     switch($SCLVal){  
  112.                         {$_ -lt 0}{$rptObj.SCLneg1 += 1}  
  113.                         0 { $rptObj.SCL0 += 1 }  
  114.                         1 { $rptObj.SCL1 += 1 }  
  115.                         2 { $rptObj.SCL2 += 1 }  
  116.                         3 { $rptObj.SCL3 += 1 }               
  117.                         4 { $rptObj.SCL4 += 1 }  
  118.                         5 { $rptObj.SCL5 += 1 }  
  119.                         6 { $rptObj.SCL6 += 1 }  
  120.                         7 { $rptObj.SCL7 += 1 }  
  121.                         8 { $rptObj.SCL8 += 1 }               
  122.                         9 { $rptObj.SCL9 += 1 }  
  123.                     }  
  124.                 }  
  125.                 else{  
  126.                     $rptObj.NoSCL +=1  
  127.                 }                          
  128.             }      
  129.             $ivItemView.Offset += $fiItems.Items.Count      
  130.         }while($fiItems.MoreAvailable -eq $true)   
  131.         $Script:rptCollection += $rptObj  
  132.     }  
  133. }  
  134.   
  135. Import-Csv -Path $args[0] | ForEach-Object{  
  136.     if($service.url -eq $null){  
  137.         $service.AutodiscoverUrl($_.SmtpAddress,{$true})   
  138.         "Using CAS Server : " + $Service.url   
  139.     }  
  140.     Try{  
  141.         Process-Mailbox -SmtpAddress $_.SmtpAddress  
  142.     }  
  143.     catch{  
  144.         LogWrite("Error processing Mailbox : " + $_.SmtpAddress + $_.Exception.Message.ToString())  
  145.     }  
  146. }  
  147. $Script:rptCollection | Export-Csv -NoTypeInformation -Path c:\temp\sclReport.csv  


Parsing out URL's in the body of a Message with EWS and Powershell

$
0
0
Sometimes when your writing an Automation script you might want to parse a certain URL from the Body of a Message. One example would be the Lync Meeting URL from an Online Meeting invitation or another might but a DropBox URL for a shared file.

To grab the Body of a Message in the EWS Managed API you need to use either Load() or LoadPropertiesForItems() if you have a number of messages your processing. Using these methods will do a GetItem (or batch GetItem) in EWS.

To parse the URL's from the HTML body markup that EWS returns you can use some RegEx to separate out all the links. Then you can use the URI class from .net to parse the matches further and identify the hosts in the URL to see if its the URL your looking for. The following sample will loop through the last 100 emails in a mailbox's and parse any Lync Meeting URL's (for Office365) or Dropbox URL's.

I've put a download of this script here

The code looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70. $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  71. # Bind to the Inbox Folder  
  72. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)     
  73. $Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  74.   
  75. #Define ItemView to retrive just 1000 Items      
  76. $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(100)      
  77. $fiItems = $service.FindItems($Inbox.Id,$ivItemView)      
  78. [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  79. foreach($Item in $fiItems.Items){        
  80.     #Process Item    
  81.     "Processing : " + $Item.Subject  
  82.     $dupChk = @{}  
  83.     $RegExHtmlLinks = "<a href=\`"(.*?)\`">"  
  84.     $matchedItems = [regex]::matches($Item.Body, $RegExHtmlLinks,[system.Text.RegularExpressions.RegexOptions]::Singleline)  
  85.     foreach($Match in $matchedItems){  
  86.         $SplitVal = $Match.Value.Split('"')  
  87.         if($SplitVal.Count -gt 0){  
  88.             $ParsedURI=[system.URI]$SplitVal[1]  
  89.             if($ParsedURI.Host -eq "meet.lync.com"){ 
  90.                 if(!$dupChk.Contains($ParsedURI.AbsoluteUri)){ 
  91.                     Write-Host -ForegroundColor Green   "LyncURL     : " + $ParsedURI.AbsoluteUri 
  92.                     $dupChk.add($ParsedURI.AbsoluteUri,0) 
  93.                 } 
  94.             }  
  95.             if($ParsedURI.Host -eq "www.dropbox.com"){ 
  96.                 if(!$dupChk.Contains($ParsedURI.AbsoluteUri)){ 
  97.                     Write-Host -ForegroundColor Blue "DropBox    : " + $ParsedURI.AbsoluteUri  
  98.                     $dupChk.add($ParsedURI.AbsoluteUri,0)  
  99.                 }  
  100.             }    
  101.         }  
  102.     }  
  103. }      

Exporting the Suggested Contacts, OWA Auto-complete Cache and Recipient Cache in Exchange 2013 with EWS and PowerShell

$
0
0
The following script creates a CSV export of Automatically generated contacts that have been saved in any of the 3 following auto contact locations

Suggested Contacts Folder which is Outlook feature that automatically creates a Contact for each address you send an email to - http://office.microsoft.com/en-au/outlook-help/automatically-add-contacts-for-everyone-that-you-send-an-email-message-HA101874367.aspx

OWA Autocomplete Cache - This is OWA version of the NickName cache, its stored in a UserConfiguration object in the Root of the Mailbox. The Addresses are stored inside an XML Streaming Property.

Recipeints Cache Folder - This is a new Exchange 2013 folder which is a hidden sub folder of the Contacts folder with a mailbox and it's purpose isn't really documented at the moment, but contains like the AutoComplete cache,  Addresses for Sent Emails.

So this script just create a CSV file with the Source, DisplayName and Email-address of any entries from these 3 locations.

I've put a download of this script here

The code looks like


  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013  
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72. $ExportCollection = @()  
  73. Write-Host "Process Recipient Cache"  
  74. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecipientCache,$MailboxName)     
  75. $RecipientCache = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  76.   
  77. $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  78. #Define ItemView to retrive just 1000 Items      
  79. $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)      
  80. $fiItems = $null      
  81. do{      
  82.     $fiItems = $service.FindItems($RecipientCache.Id,$ivItemView)      
  83.     [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  84.     foreach($Item in $fiItems.Items){       
  85.         if($Item -is [Microsoft.Exchange.WebServices.Data.Contact]){  
  86.             $expObj = "" | select Source,DisplayName,Email1DisplayName,Email1Type,Email1EmailAddress  
  87.             $expObj.Source = "RecipientCache"  
  88.             $expObj.DisplayName = $Item.DisplayName  
  89.             if($Item.EmailAddresses.Contains([Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1)){                  
  90.                 $expObj.Email1DisplayName = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Name  
  91.                 $expObj.Email1Type = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].RoutingType  
  92.                 $expObj.Email1EmailAddress = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address  
  93.             }  
  94.             $ExportCollection += $expObj  
  95.         }  
  96.     }      
  97.     $ivItemView.Offset += $fiItems.Items.Count      
  98. }while($fiItems.MoreAvailable -eq $true)   
  99.   
  100. Write-Host "Process Suggested Contacts"  
  101. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)   
  102. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)  
  103. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Suggested Contacts")  
  104. $findFolderResults = $service.FindFolders($folderid,$SfSearchFilter,$fvFolderView)  
  105.   
  106. if($findFolderResults.Folders.Count -gt 0){  
  107.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  108.     #Define ItemView to retrive just 1000 Items      
  109.     $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)      
  110.     $fiItems = $null      
  111.     do{      
  112.         $fiItems = $service.FindItems($findFolderResults.Folders[0].Id,$ivItemView)      
  113.         [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  114.         foreach($Item in $fiItems.Items){       
  115.             if($Item -is [Microsoft.Exchange.WebServices.Data.Contact]){  
  116.                 $expObj = "" | select Source,DisplayName,Email1DisplayName,Email1Type,Email1EmailAddress  
  117.                 $expObj.Source = "Suggested Contacts"  
  118.                 $expObj.DisplayName = $Item.DisplayName  
  119.                 if($Item.EmailAddresses.Contains([Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1)){                  
  120.                     $expObj.Email1DisplayName = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Name  
  121.                     $expObj.Email1Type = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].RoutingType  
  122.                     $expObj.Email1EmailAddress = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address  
  123.                 }  
  124.                 $ExportCollection += $expObj  
  125.             }  
  126.         }      
  127.         $ivItemView.Offset += $fiItems.Items.Count      
  128.     }while($fiItems.MoreAvailable -eq $true)   
  129. }  
  130. Write-Host "Process OWA AutocompleteCache"  
  131. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)   
  132. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)       
  133. #Specify the Calendar folder where the FAI Item is    
  134. $UsrConfig = [Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($service"OWA.AutocompleteCache"$folderid, [Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All)    
  135. #Get the XML in String Format    
  136. $acXML = [System.Text.Encoding]::UTF8.GetString($UsrConfig.XmlData)    
  137. #Deal with the first character being a Byte Order Mark    
  138. $boMark = $acXML.SubString(0,1)    
  139. #Parse the XML    
  140. [XML]$acXML = $acXML.SubString(1)    
  141. foreach($AcEnt in $acXML.AutoCompleteCache.Entry){  
  142.     $expObj = "" | select Source,DisplayName,Email1DisplayName,Email1Type,Email1EmailAddress  
  143.     $expObj.Source = "OWA AutocompleteCache"  
  144.     $expObj.DisplayName = $AcEnt.displayName  
  145.     $expObj.Email1DisplayName= $AcEnt.displayName  
  146.     $expObj.Email1Type= "SMTP"  
  147.     $expObj.Email1EmailAddress= $AcEnt.smtpAddr  
  148.     $ExportCollection +=$expObj   
  149. }  
  150. $fnFileName = "c:\temp\" + $MailboxName + "AC-CacheExport.csv" 
  151. $ExportCollection | Export-Csv -NoTypeInformation -Path $fnFileName 
  152. "Exported to " + $fnFileName  

Creating a Shared Calendar Shortcut (WunderBar Link) with EWS and Powershell

$
0
0
Shared Calendar Shortcuts eg



are an Outlook and OWA  feature that can be handy to automate if you need to deploy a number of these to new (or existing) mailboxes and you don't want to go through the invitation/accept procedure or manually adding each shortcut.

While there are no supported operations in EWS for creating these type of objects, it can be achieved by setting the Extended MAPI properties that constitute the shortcut. The downside of this is that it wouldn't ever be considered supported if it all goes horribly wrong. The properties involved in the shortcut are documented in the following protocol document http://msdn.microsoft.com/en-us/library/ee157359(v=exchg.80).aspx.

For a couple of these properties the values you need to get can't be obtained directly in EWS so some others tricks are needed.

The PidTagWlinkAddressBookEID property contains the MAPI address Book EntryId for the shared Calendar your connecting to. The Address Book EntryID format is documented here http://msdn.microsoft.com/en-us/library/ee160588%28v=exchg.80%29.aspx so to construct this in EWS you need to get the LegacyExchangeDN from AutoDiscover and then appended the ProviderUID,Flag and Type information which for a normal user will be the same. (Note if you are trying to connect to an object other then a user mailbox.

The PidTagWlinkAddressBookStoreEID proeprty contains the MAPI StoreEntryID of the users where your creating the ShortCut. While you can get this property in EWS the value you get isn't correct because EWS uses different wrapper and providerID values. So instead using the format documented in http://msdn.microsoft.com/en-us/library/ee203516%28v=exchg.80%29.aspx you can construct this which involves getting the LegacyExchangeDN and the ServerName for Autodiscover and then building the identifier.

The other properties are pretty static for shared calendars.

The ShortCuts them selves are saved in a Non_IPM_Subtree folder called common views

(Special thanks also to Neil Doody for helping with property definitions in this post and script)

So the following script takes two commandline parameter the first is the Mailbox where you want the shortcut to be created and the second is the Mailbox which has the Calendar you want the shortcut to point to so you would run it like

./createWBarCal.ps1 user@domain.com target@domain.com

 The script will check the CommonViews folder to see if a SharedFolder shortcut already exists for the target Mailbox AddressId and FolderType and if no ShortCut exists and it will attempt to create one.

As i mentioned before as this script is completely unsupported and for the most part untested and should only be considered safe for testing in a development\test environment. It also assume you have Autodiscover working if not your this isn't going to work well.

 I've put a download of the script here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2. $TargetCalendarMailbox = $args[1]  
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72.   
  73. function GetAutoDiscoverSettings{  
  74.     param (  
  75.             $adEmailAddress = "$( throw 'emailaddress is a mandatory Parameter' )",  
  76.             $Credentials = "$( throw 'Credentials is a mandatory Parameter' )"  
  77.           )  
  78.     process{  
  79.         $adService = New-Object Microsoft.Exchange.WebServices.AutoDiscover.AutodiscoverService($ExchangeVersion);  
  80.         $adService.Credentials = $Credentials  
  81.         $adService.EnableScpLookup = $false;  
  82.         $adService.RedirectionUrlValidationCallback = {$true}  
  83.         $UserSettings = new-object Microsoft.Exchange.WebServices.Autodiscover.UserSettingName[] 3  
  84.         $UserSettings[0] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN  
  85.         $UserSettings[1] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer  
  86.         $UserSettings[2] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDisplayName  
  87.         $adResponse = $adService.GetUserSettings($adEmailAddress , $UserSettings);  
  88.         return $adResponse  
  89.     }  
  90. }  
  91. function GetAddressBookId{  
  92.     param (  
  93.             $AutoDiscoverSettings = "$( throw 'AutoDiscoverSettings is a mandatory Parameter' )"  
  94.           )  
  95.     process{  
  96.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  97.         $userdnHexChar = $userdnString.ToCharArray();  
  98.         foreach ($element in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}  
  99.         $Provider = "00000000DCA740C8C042101AB4B908002B2FE1820100000000000000"  
  100.         $userdnStringHex = $Provider + $userdnStringHex + "00"  
  101.         return $userdnStringHex  
  102.     }  
  103. }  
  104. function GetStoreId{  
  105.     param (  
  106.             $AutoDiscoverSettings = "$( throw 'AutoDiscoverSettings is a mandatory Parameter' )"  
  107.           )  
  108.     process{  
  109.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  110.         $userdnHexChar = $userdnString.ToCharArray();  
  111.         foreach ($element in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}   
  112.         $serverNameString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer]  
  113.         $serverNameHexChar = $serverNameString.ToCharArray();  
  114.         foreach ($element in $serverNameHexChar) {$serverNameStringHex = $serverNameStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}  
  115.         $flags = "00000000"  
  116.         $ProviderUID = "38A1BB1005E5101AA1BB08002B2A56C2"  
  117.         $versionFlag = "0000"  
  118.         $DLLFileName = "454D534D44422E444C4C00000000"  
  119.         $WrappedFlags = "00000000"  
  120.         $WrappedProviderUID = "1B55FA20AA6611CD9BC800AA002FC45A"  
  121.         $WrappedType = "0C000000"  
  122.         $StoredIdStringHex = $flags + $ProviderUID + $versionFlag + $DLLFileName + $WrappedFlags + $WrappedProviderUID + $WrappedType + $serverNameStringHex + "00" + $userdnStringHex + "00"  
  123.         return $StoredIdStringHex  
  124.     }  
  125. }  
  126.   
  127.   
  128. function hex2binarray($hexString){  
  129.     $i = 0  
  130.     [byte[]]$binarray = @()  
  131.     while($i -le $hexString.length - 2){  
  132.         $strHexBit = ($hexString.substring($i,2))  
  133.         $binarray += [byte]([Convert]::ToInt32($strHexBit,16))  
  134.         $i = $i + 2  
  135.     }  
  136.     return ,$binarray  
  137. }  
  138. function ConvertId($EWSid){      
  139.     $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternateId        
  140.     $aiItem.Mailbox = $MailboxName        
  141.     $aiItem.UniqueId = $EWSid     
  142.     $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::EWSId;        
  143.     return $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::StoreId)       
  144. }   
  145.   
  146. #PropDefs   
  147. $pidTagStoreEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(4091, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  148. $PidTagNormalizedSubject = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0E1D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);   
  149. $PidTagWlinkType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6849, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  150. $PidTagWlinkFlags = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684A, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  151. $PidTagWlinkOrdinal = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684B, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  152. $PidTagWlinkFolderType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684F, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  153. $PidTagWlinkSection = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6852, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  154. $PidTagWlinkGroupHeaderID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6842, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  155. $PidTagWlinkSaveStamp = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6847, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  156. $PidTagWlinkGroupName = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6851, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  157. $PidTagWlinkStoreEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684E, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  158. $PidTagWlinkGroupClsid = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6850, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  159. $PidTagWlinkEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684C, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  160. $PidTagWlinkRecordKey = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684D, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  161. $PidTagWlinkCalendarColor = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6853, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  162. $PidTagWlinkAddressBookEID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6854,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  163. $PidTagWlinkROGroupType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6892,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  164. $PidTagWlinkAddressBookStoreEID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6891,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  165.   
  166.   
  167. #Get the TargetUsers Calendar  
  168. # Bind to the Calendar Folder  
  169. $fldPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  170. $fldPropset.Add($pidTagStoreEntryId);  
  171. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$TargetCalendarMailbox)     
  172. $TargetCalendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$fldPropset)  
  173. #Check for existing ShortCut for TargetMailbox  
  174. #Get AddressBook Id for TargetUser  
  175. Write-Host ("Getting Autodiscover Settings Target")  
  176. Write-Host ("Getting Autodiscover Settings Mailbox")  
  177. $adset = GetAutoDiscoverSettings -adEmailAddress $MailboxName -Credentials $creds  
  178. $storeID = ""  
  179. if($adset -is [Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverResponse]){  
  180.     Write-Host ("Get StoreId")  
  181.     $storeID = GetStoreId -AutoDiscoverSettings $adset  
  182. }  
  183. $adset = $null  
  184. $abTargetABEntryId = ""  
  185. $adset = GetAutoDiscoverSettings -adEmailAddress $TargetCalendarMailbox -Credentials $creds  
  186. if($adset -is [Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverResponse]){  
  187.     Write-Host ("Get AB Id")  
  188.     $abTargetABEntryId = GetAddressBookId -AutoDiscoverSettings $adset  
  189.     $SharedUserDisplayName =  $adset.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDisplayName]  
  190. }  
  191. Write-Host ("Getting CommonVeiwFolder")  
  192. #Get CommonViewFolder  
  193. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  194. $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)    
  195. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)   
  196. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Common Views")   
  197. $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)   
  198. if ($findFolderResults.TotalCount -gt 0){   
  199.     $ExistingShortCut = $false  
  200.     $cvCommonViewsFolder = $findFolderResults.Folders[0]  
  201.     #Define ItemView to retrive just 1000 Items      
  202.     #Find Items that are unread  
  203.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  204.     $psPropset.add($PidTagWlinkAddressBookEID)  
  205.     $psPropset.add($PidTagWlinkFolderType)  
  206.     $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)     
  207.     $ivItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated  
  208.     $ivItemView.PropertySet = $psPropset  
  209.     $fiItems = $service.FindItems($cvCommonViewsFolder.Id,$ivItemView)      
  210.     foreach($Item in $fiItems.Items){  
  211.         $aeidVal = $null  
  212.         if($Item.TryGetProperty($PidTagWlinkAddressBookEID,[ref]$aeidVal)){  
  213.                 $fldType = $null  
  214.                 if($Item.TryGetProperty($PidTagWlinkFolderType,[ref]$fldType)){  
  215.                     if([System.BitConverter]::ToString($fldType).Replace("-","") -eq "0278060000000000C000000000000046"){  
  216.                         if([System.BitConverter]::ToString($aeidVal).Replace("-","") -eq $abTargetABEntryId){  
  217.                             $ExistingShortCut = $true  
  218.                             Write-Host "Found existing Shortcut"  
  219.                             ###$Item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)  
  220.                         }  
  221.                     }  
  222.                 }  
  223.             }                               
  224.     }  
  225.     if($ExistingShortCut -eq $false){  
  226.         If($storeID.length -gt 5 -band $abTargetABEntryId.length -gt 5){  
  227.             $objWunderBarLink = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service    
  228.             $objWunderBarLink.Subject = $SharedUserDisplayName    
  229.             $objWunderBarLink.ItemClass = "IPM.Microsoft.WunderBar.Link"    
  230.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkAddressBookEID,(hex2binarray $abTargetABEntryId))    
  231.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkAddressBookStoreEID,(hex2binarray $storeID))    
  232.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkCalendarColor,-1)  
  233.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkFlags,0)  
  234.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkGroupName,"Shared Calendars")  
  235.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkFolderType,(hex2binarray "0278060000000000C000000000000046"))    
  236.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkGroupClsid,(hex2binarray "B9F0060000000000C000000000000046"))    
  237.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkROGroupType,-1)  
  238.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkSection,3)    
  239.             $objWunderBarLink.SetExtendedProperty($PidTagWlinkType,2)    
  240.             $objWunderBarLink.IsAssociated = $true  
  241.             $objWunderBarLink.Save($findFolderResults.Folders[0].Id)  
  242.             Write-Host ("ShortCut Created for - " + $SharedUserDisplayName)  
  243.         }  
  244.         else{  
  245.             Write-Host ("Error with Id's")  
  246.         }  
  247.     }  
  248. }  






Turning off the Reading / Preview Pane on all folders in OWA in Exchange 2013 and Exchange Online in EWS with Powershell

$
0
0
Exchange 2013 and Exchange Online has a cool new feature in OWA to let you control the Reading pane setting on all folders in your Mailbox in OWA eg. (in previous versions you had to manage it yourself per folder).

When you set this setting in OWA and check the "Apply to all folders" box, in your mailbox it sets a configuration setting called GlobalReadingPanePosition in the OWA.UserOptions FAI (Folder Associated Item) in the Non_IPM Root of your Mailbox. The following values control the position of the Reading Pane on all folders

0 - Hides the Reading Pane
1 - Shows the Reading Pane on the Rights (this is the default)
2 - Shows the Reading Pane at the bottom

To Access this FAI Item in EWS you use the UserConfiguration class which makes accessing the Roaming Dictionary structure where this particular value is held really easy. Eg the following script will turn the Reading pane off on all Folders in the mailbox its ran against. I've put a download of this script here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72.   
  73. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)       
  74. #Specify the Root folder where the FAI Item is    
  75. $UsrConfig = [Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($service"OWA.UserOptions"$folderid, [Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All)    
  76. if($UsrConfig.Dictionary.ContainsKey("GlobalReadingPanePosition")){    
  77.     $UsrConfig.Dictionary["GlobalReadingPanePosition"] = 0    
  78. }    
  79. else{    
  80.     $UsrConfig.Dictionary.Add("GlobalReadingPanePosition",0)    
  81. }  
  82. $UsrConfig.Update()    
  83. "Item updated"  



Clearing the RSS Feeds folder in Exchange using EWS and Powershell

$
0
0
RSS feeds in Outlook is a nice feature that was added in Outlook, but for some organizations it may not be a desirable feature. While you can't delete the RSS Feeds folder itself as its on of Outlooks default folders you can easily remove each of the feeds and items from these folders using EWS's Empty folder operation http://msdn.microsoft.com/en-us/library/exchange/ff797574(v=exchg.80).aspx. A couple of months back I posted a Language independent way of getting the RSS feeds folder using the PidTagAdditionalRenEntryIdsEx property http://gsexdev.blogspot.com.au/2013/01/using-pidtagadditionalrenentryidsex.html . This script can be easily modified to Empty the RSSFeeds folder eg  the following script binds to the RSS Feeds folder and use the Empty Operation with the parameter to also delete all sub folders under the RSS Feeds Folder. - I've put a download of this script here.


  1. ## Get the Mailbox to Access from the 1st commandline argument    
  2.     
  3. $MailboxName = $args[0]    
  4.     
  5. ## Load Managed API dll      
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"      
  7.       
  8. ## Set Exchange Version      
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2      
  10.       
  11. ## Create Exchange Service Object      
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)      
  13.       
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials      
  15.       
  16. #Credentials Option 1 using UPN for the windows Account      
  17. $psCred = Get-Credential      
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())      
  19. $service.Credentials = $creds          
  20.       
  21. #Credentials Option 2      
  22. #service.UseDefaultCredentials = $true      
  23.       
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates      
  25.       
  26. ## Code From http://poshcode.org/624    
  27. ## Create a compilation environment    
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider    
  29. $Compiler=$Provider.CreateCompiler()    
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters    
  31. $Params.GenerateExecutable=$False    
  32. $Params.GenerateInMemory=$True    
  33. $Params.IncludeDebugInformation=$False    
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null    
  35.     
  36. $TASource=@ 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{  
  38.     public class TrustAll : System.Net.ICertificatePolicy {  
  39.       public TrustAll() {   
  40.       }  
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp,  
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,   
  43.         System.Net.WebRequest req, int problem) {  
  44.         return true;  
  45.       }  
  46.     }  
  47.   }  
  48. '@     
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)    
  50. $TAAssembly=$TAResults.CompiledAssembly    
  51.     
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager    
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")    
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll    
  55.     
  56. ## end code from http://poshcode.org/624    
  57.       
  58. ## 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      
  59.       
  60. #CAS URL Option 1 Autodiscover      
  61. $service.AutodiscoverUrl($MailboxName,{$true})      
  62. "Using CAS Server : " + $Service.url       
  63.        
  64. #CAS URL Option 2 Hardcoded      
  65.       
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"      
  67. #$service.Url = $uri        
  68.       
  69. ## Optional section for Exchange Impersonation      
  70.       
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)     
  72. $PidTagAdditionalRenEntryIdsEx = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x36D9, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)    
  73. $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  74. $psPropset.Add($PidTagAdditionalRenEntryIdsEx)    
  75.     
  76. # Bind to the NON_IPM_ROOT Root folder      
  77. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)       
  78. $NON_IPM_ROOT = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$psPropset)    
  79. $binVal = $null;    
  80. $AdditionalRenEntryIdsExCol = @{}    
  81. if($NON_IPM_ROOT.TryGetProperty($PidTagAdditionalRenEntryIdsEx,[ref]$binVal)){    
  82.     $hexVal = [System.BitConverter]::ToString($binVal).Replace("-","");    
  83.     ##Parse Binary Value first word is Value type Second word is the Length of the Entry    
  84.     $Sval = 0;    
  85.     while(($Sval+8) -lt $hexVal.Length){    
  86.         $PtypeVal = $hexVal.SubString($Sval,4)    
  87.         $PtypeVal = $PtypeVal.SubString(2,2) + $PtypeVal.SubString(0,2)    
  88.         $Sval +=12;    
  89.         $PropLengthVal = $hexVal.SubString($Sval,4)    
  90.         $PropLengthVal = $PropLengthVal.SubString(2,2) + $PropLengthVal.SubString(0,2)    
  91.         $PropLength = [Convert]::ToInt64($PropLengthVal, 16)    
  92.         $Sval +=4;    
  93.         $ProdIdEntry = $hexVal.SubString($Sval,($PropLength*2))    
  94.         $Sval += ($PropLength*2)    
  95.         #$PtypeVal + " : " + $ProdIdEntry    
  96.         $AdditionalRenEntryIdsExCol.Add($PtypeVal,$ProdIdEntry)     
  97.     }       
  98. }    
  99.     
  100. function ConvertFolderid($hexId){    
  101.     $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternateId      
  102.     $aiItem.Mailbox = $MailboxName      
  103.     $aiItem.UniqueId = $hexId    
  104.     $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId;      
  105.     return $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EWSId)     
  106. }    
  107.   
  108. #Define Function to convert String to FolderPath    
  109. function ConvertToString($ipInputString){    
  110.     $Val1Text = ""    
  111.     for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  112.             $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  113.             $clInt++    
  114.     }    
  115.     return $Val1Text    
  116. }   
  117.   
  118.     
  119. if($AdditionalRenEntryIdsExCol.ContainsKey("8001")){    
  120.     $siId = ConvertFolderid($AdditionalRenEntryIdsExCol["8001"])    
  121.     $RSSFolderID = new-object Microsoft.Exchange.WebServices.Data.FolderId($siId.UniqueId.ToString())    
  122.     $RSSFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$RSSFolderID)   
  123.     if($RSSFolder -ne $null){  
  124.         "Deleteing : " + $RSSFolder.ChildFolderCount.ToString() + " Feeds"  
  125.         $RSSFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete, $true);  
  126.     }  
  127. }    

Mapping what Folders are in use in ActiveSync with EWS and Powershell

$
0
0
I came across an interesting Blog post this week from Jim Martin

http://blogs.technet.com/b/tips_from_the_inside/archive/2013/06/17/activesync-mapping-a-collection-id-to-a-mailbox-folder.aspx

which explains how the ActiveSync Folders located under the device folder in ExchangeSyncData folder tree map back to the real Mailbox folder that is being synchronized or accessed via ActiveSync. This information is pretty useful for a number of things so I thought it would be useful to put a script together to automate and create a report of these folder mapping using EWS and Powershell.

How it works is the ExchangeSyncData is located in the NON_IPM_Subtree of a mailbox so a few FindFolder calls are needed to find this folder and then get all the Folders associated with any ActiveSync devices for the Mailbox. The 0x7C030102 property is set on ActiveSync Collection folder and as in Jim's post if you take the first byte and last byte off the value from this property you have the HexEntryId of the folder. In EWS you can then use convertId to covert it to the ewsID of the Folder and then bind to the FolderId to work out which folder is being synced. Basically what you end up with is a report that looks like this

The LastModified column is the time the ActiveSync folder was last modified which should relate to the last time the folder was accessed over ActiveSync (from my observation anyway). The Device column just comes from the DisplayName of the Root folder from the device

I've put a download of this script here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $AsFolderReport = @()  
  5.   
  6. ## Load Managed API dll    
  7. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  8.     
  9. ## Set Exchange Version    
  10. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  11.     
  12. ## Create Exchange Service Object    
  13. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  14.     
  15. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  16.     
  17. #Credentials Option 1 using UPN for the windows Account    
  18. $psCred = Get-Credential    
  19. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  20. $service.Credentials = $creds        
  21.     
  22. #Credentials Option 2    
  23. #service.UseDefaultCredentials = $true    
  24.     
  25. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  26.     
  27. ## Code From http://poshcode.org/624  
  28. ## Create a compilation environment  
  29. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  30. $Compiler=$Provider.CreateCompiler()  
  31. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  32. $Params.GenerateExecutable=$False  
  33. $Params.GenerateInMemory=$True  
  34. $Params.IncludeDebugInformation=$False  
  35. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  36.   
  37. $TASource=@' 
  38.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  39.     public class TrustAll : System.Net.ICertificatePolicy { 
  40.       public TrustAll() {  
  41.       } 
  42.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  43.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  44.         System.Net.WebRequest req, int problem) { 
  45.         return true; 
  46.       } 
  47.     } 
  48.   } 
  49. '@   
  50. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  51. $TAAssembly=$TAResults.CompiledAssembly  
  52.   
  53. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  54. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  55. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  56.   
  57. ## end code from http://poshcode.org/624  
  58.     
  59. ## 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    
  60.     
  61. #CAS URL Option 1 Autodiscover    
  62. $service.AutodiscoverUrl($MailboxName,{$true})    
  63. "Using CAS Server : " + $Service.url     
  64.      
  65. #CAS URL Option 2 Hardcoded    
  66.     
  67. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  68. #$service.Url = $uri      
  69.     
  70. ## Optional section for Exchange Impersonation    
  71.     
  72. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  73. # Bind to the MsgFolderRoot folder    
  74. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  75. $MsgRoot = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  76.   
  77. function ConvertId{      
  78.     param (  
  79.             $HexId = "$( throw 'HexId is a mandatory Parameter' )"  
  80.           )  
  81.     process{  
  82.         $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternateId        
  83.         $aiItem.Mailbox = $MailboxName        
  84.         $aiItem.UniqueId = $HexId     
  85.         $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId        
  86.         $convertedId = $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EwsId)   
  87.         return $convertedId.UniqueId  
  88.     }  
  89. }  
  90.   
  91. function GetFolderPath{  
  92.     param (  
  93.         $EWSFolder = "$( throw 'Folder is a mandatory Parameter' )"  
  94.     )  
  95.     process{  
  96.         $foldpathval = $null    
  97.         $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  98.         if ($EWSFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  99.         {    
  100.             $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  101.             $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  102.             $hexString = $hexArr -join ''    
  103.         $hexString = $hexString.Replace("EFBFBE""5C")    
  104.             $fpath = ConvertToString($hexString)   
  105.         return $fpath  
  106.         }    
  107.     }  
  108. }  
  109. $fldMappingHash = @{}  
  110. #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  111. $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1)    
  112. #Deep Transval will ensure all folders in the search path are returned    
  113. $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Shallow;    
  114. #The Search filter will exclude any Search Folders    
  115. $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"ExchangeSyncData")    
  116. $asFolderRoot = $Service.FindFolders($MsgRoot.Id,$sfSearchFilter,$fvFolderView)    
  117. if($asFolderRoot.Folders.Count -eq 1){  
  118.     #Define Function to convert String to FolderPath    
  119.     function ConvertToString($ipInputString){    
  120.         $Val1Text = ""    
  121.         for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  122.                 $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  123.                 $clInt++    
  124.         }    
  125.         return $Val1Text    
  126.     }   
  127.       
  128.     #Define Extended properties    
  129.     $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  130.     #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  131.     $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  132.     #Deep Transval will ensure all folders in the search path are returned    
  133.     $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;    
  134.     $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  135.     $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  136.     $CollectionIdProp = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x7C03, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  137.     $LastModifiedTime = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3008, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime)  
  138.     #Add Properties to the  Property Set    
  139.     $psPropertySet.Add($PR_Folder_Path);    
  140.     $psPropertySet.Add($CollectionIdProp);  
  141.     $psPropertySet.Add($LastModifiedTime);    
  142.     $fvFolderView.PropertySet = $psPropertySet;    
  143.     #The Search filter will exclude any Search Folders    
  144.     $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  145.     $fiResult = $null    
  146.     #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox    
  147.     do {    
  148.         $fiResult = $Service.FindFolders($asFolderRoot.Folders[0].Id,$sfSearchFilter,$fvFolderView)    
  149.         foreach($ffFolder in $fiResult.Folders){   
  150.             if(!$fldMappingHash.ContainsKey($ffFolder.Id.UniqueId)){  
  151.                 $fldMappingHash.Add($ffFolder.Id.UniqueId,$ffFolder)  
  152.             }  
  153.             $asFolderPath = ""  
  154.             $asFolderPath = (GetFolderPath -EWSFolder $ffFolder)  
  155.             "FolderPath : " + $asFolderPath  
  156.             $collectVal = $null  
  157.             if($ffFolder.TryGetProperty($CollectionIdProp,[ref]$collectVal)){  
  158.                 $HexEntryId = [System.BitConverter]::ToString($collectVal).Replace("-","").Substring(2)  
  159.                 $ewsFolderId = ConvertId -HexId ($HexEntryId.SubString(0,($HexEntryId.Length-2)))  
  160.                 try{  
  161.                     $fldReport = "" | Select Mailbox,Device,AsFolderPath,MailboxFolderPath,LastModified  
  162.                     $fldReport.Mailbox = $MailboxName  
  163.                     $fldReport.Device = $fldMappingHash[$ffFolder.ParentFolderId.UniqueId].DisplayName  
  164.                     $fldReport.AsFolderPath = $asFolderPath  
  165.                     $folderMapId= new-object Microsoft.Exchange.WebServices.Data.FolderId($ewsFolderId)     
  166.                     $MappedFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderMapId,$psPropertySet)  
  167.                     $MappedFolderPath = (GetFolderPath -EWSFolder $MappedFolder)  
  168.                     $fldReport.MailboxFolderPath = $MappedFolderPath  
  169.                     $LastModifiedVal = $null  
  170.                     if($ffFolder.TryGetProperty($LastModifiedTime,[ref]$LastModifiedVal)){  
  171.                         Write-Host ("Last-Modified : " +  $LastModifiedVal.ToLocalTime().ToString())  
  172.                         $fldReport.LastModified = $LastModifiedVal.ToLocalTime().ToString()  
  173.                     }  
  174.                     Write-Host $MappedFolderPath  
  175.                     $AsFolderReport += $fldReport  
  176.                 }  
  177.                 catch{  
  178.                       
  179.                 }  
  180.                 $ewsFolderId  
  181.             }  
  182.             #Process folder here  
  183.         }   
  184.         $fvFolderView.Offset += $fiResult.Folders.Count  
  185.     }while($fiResult.MoreAvailable -eq $true)    
  186.       
  187.       
  188. }  
  189. $reportFile = "c:\temp\$MailboxName-asFolders.csv"  
  190. $AsFolderReport | Export-Csv -NoTypeInformation -Path $reportFile  
  191. Write-Host ("Report wrtten to " + $reportFile)  

Using Sender Flags in EWS

$
0
0
Sender Flags are an Outlook Feature that allow you to set a follow up flag on Message when your sending it as well as being able to set a Recipient Followup flag if you want to. (eg)


What happens in Exchange when you set a Sender Flag on a Message is documented in the Informational Flagging Protocol document http://msdn.microsoft.com/en-us/library/cc433487(v=exchg.80).aspx . If you want to do the same thing to a Message your sending in EWS there is nothing in the strongly typed classes to help out so you need to manually set two of the extended properties that are documented in that protocol document before you send the message and the Store will do the rest on submit. (More specifically what happens with the properties involved is documented here http://msdn.microsoft.com/en-us/library/ee217246(v=exchg.80).aspx )

The important properties that are involved with sender flags are the PidTagSwappedToDoData which is used to Store the information about what sender flags your want (eg in Outlook configured via the above dialogue box).  This is a binary property containing an number of Flags, The text for the Message Flags and the DateTime values for the Start and DueDate. The full structure of the property is documented in http://msdn.microsoft.com/en-us/library/ee201575(v=exchg.80).aspx

The DateTime values used in this Property are stored as a 4-byte integer that are expressed as the number of minutes since 00:00:00 on January 1, 1601, in UTC. To get this Integer the method I used is a TimeSpan between the Date you want and January 1, 1601, in UTC, you can then get the TotalMinutes for the timespan as an Integer.

 TimeSpan ts = new DateTime(2013,12,01,0,0,0,0,DateTimeKind.Utc) - new DateTime(1601, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

The other important property is the PidTagSwappedToDoStore which is used by the Store after the Message is submitted. To use this is EWS you need to construct it based on the format documented in http://msdn.microsoft.com/en-us/library/ee203516%28v=exchg.80%29.aspx . The information necessary for this can be obtain through AutoDiscover. 

I've created a Powershell and EWS Sample for setting a Sender Flag on a Message with a Start and DueDate. I've put a download of this script here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. $SenderFlagText = "This is a Test Blah Blah"  
  6. $FlagStartDate = (Get-Date).AddDays(1)  
  7. $FlagDueDate = (Get-Date).AddDays(7)  
  8.  
  9.  
  10. ## Load Managed API dll    
  11. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  12.    
  13. ## Set Exchange Version    
  14. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  15.    
  16. ## Create Exchange Service Object    
  17. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  18.    
  19. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  20.    
  21. #Credentials Option 1 using UPN for the windows Account    
  22. $psCred = Get-Credential    
  23. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  24. $service.Credentials = $creds        
  25.    
  26. #Credentials Option 2    
  27. #service.UseDefaultCredentials = $true    
  28.    
  29. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  30.    
  31. ## Code From http://poshcode.org/624  
  32. ## Create a compilation environment  
  33. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  34. $Compiler=$Provider.CreateCompiler()  
  35. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  36. $Params.GenerateExecutable=$False  
  37. $Params.GenerateInMemory=$True  
  38. $Params.IncludeDebugInformation=$False  
  39. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  40.   
  41. $TASource=@' 
  42.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  43.     public class TrustAll : System.Net.ICertificatePolicy { 
  44.       public TrustAll() {  
  45.       } 
  46.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  47.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  48.         System.Net.WebRequest req, int problem) { 
  49.         return true; 
  50.       } 
  51.     } 
  52.   } 
  53. '@   
  54. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  55. $TAAssembly=$TAResults.CompiledAssembly  
  56.  
  57. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  58. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  59. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  60.  
  61. ## end code from http://poshcode.org/624  
  62.    
  63. ## 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    
  64.    
  65. #CAS URL Option 1 Autodiscover    
  66. $service.AutodiscoverUrl($MailboxName,{$true})    
  67. "Using CAS Server : " + $Service.url     
  68.     
  69. #CAS URL Option 2 Hardcoded    
  70.    
  71. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  72. #$service.Url = $uri      
  73.    
  74. ## Optional section for Exchange Impersonation    
  75.   
  76. function hex2binarray($hexString){  
  77.     $i = 0  
  78.     [byte[]]$binarray = @()  
  79.     while($i -le $hexString.length - 2){  
  80.         $strHexBit = ($hexString.substring($i,2))  
  81.         $binarray += [byte]([Convert]::ToInt32($strHexBit,16))  
  82.         $i = $i + 2  
  83.     }  
  84.     return ,$binarray  
  85. }  
  86.   
  87. function GetAutoDiscoverSettings{  
  88.     param (  
  89.             $adEmailAddress = "$( throw 'emailaddress is a mandatory Parameter' )",  
  90.             $Credentials = "$( throw 'Credentials is a mandatory Parameter' )"  
  91.           )  
  92.     process{  
  93.         $adService = New-Object Microsoft.Exchange.WebServices.AutoDiscover.AutodiscoverService($ExchangeVersion);  
  94.         $adService.Credentials = $Credentials  
  95.         $adService.EnableScpLookup = $false;  
  96.         $adService.RedirectionUrlValidationCallback = {$true}  
  97.         $UserSettings = new-object Microsoft.Exchange.WebServices.Autodiscover.UserSettingName[] 3  
  98.         $UserSettings[0] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN  
  99.         $UserSettings[1] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer  
  100.         $UserSettings[2] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDisplayName  
  101.         $adResponse = $adService.GetUserSettings($adEmailAddress , $UserSettings);  
  102.         return $adResponse  
  103.     }  
  104. }  
  105. function GetAddressBookId{  
  106.     param (  
  107.             $AutoDiscoverSettings = "$( throw 'AutoDiscoverSettings is a mandatory Parameter' )"  
  108.           )  
  109.     process{  
  110.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  111.         $userdnHexChar = $userdnString.ToCharArray();  
  112.         foreach ($element in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}  
  113.         $Provider = "00000000DCA740C8C042101AB4B908002B2FE1820100000000000000"  
  114.         $userdnStringHex = $Provider + $userdnStringHex + "00"  
  115.         return $userdnStringHex  
  116.     }  
  117. }  
  118. function GetStoreId{  
  119.     param (  
  120.             $AutoDiscoverSettings = "$( throw 'AutoDiscoverSettings is a mandatory Parameter' )"  
  121.           )  
  122.     process{  
  123.         $userdnString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::UserDN]  
  124.         $userdnHexChar = $userdnString.ToCharArray();  
  125.         foreach ($element in $userdnHexChar) {$userdnStringHex = $userdnStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}   
  126.         $serverNameString = $AutoDiscoverSettings.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer]  
  127.         $serverNameHexChar = $serverNameString.ToCharArray();  
  128.         foreach ($element in $serverNameHexChar) {$serverNameStringHex = $serverNameStringHex + [System.String]::Format("{0:X}", [System.Convert]::ToUInt32($element))}  
  129.         $flags = "00000000"  
  130.         $ProviderUID = "38A1BB1005E5101AA1BB08002B2A56C2"  
  131.         $versionFlag = "0000"  
  132.         $DLLFileName = "454D534D44422E444C4C00000000"  
  133.         $WrappedFlags = "00000000"  
  134.         $WrappedProviderUID = "1B55FA20AA6611CD9BC800AA002FC45A"  
  135.         $WrappedType = "0C000000"  
  136.         $StoredIdStringHex = $flags + $ProviderUID + $versionFlag + $DLLFileName + $WrappedFlags + $WrappedProviderUID + $WrappedType + $serverNameStringHex + "00" + $userdnStringHex + "00"  
  137.         return $StoredIdStringHex  
  138.     }  
  139. }  
  140.   
  141.   
  142. function GetPidTagSwappedToDoData {  
  143.     param (  
  144.         $FlagText = "$( throw 'FlagText is a mandatory Parameter' )",  
  145.         $StartTime = "$( throw 'StartTime is a mandatory Parameter' )",  
  146.         $DueTime = "$( throw 'DueTime is a mandatory Parameter' )"  
  147.     )  
  148.     process{  
  149.         $todoTimeFlagged  = "01000000";  
  150.         $PidLidFlagRequest = ""  
  151.         $PidLidFlagRequestHexChar = $FlagText.ToCharArray();  
  152.         $PidLidFlagRequest = [System.BitConverter]::ToString([System.Text.UnicodeEncoding]::Unicode.GetBytes($FlagText)).Replace("-","")  
  153.         #Pad Flag to 512 Bytes  
  154.         for ($padCnt = $PidLidFlagRequest.Length / 2; $padCnt -lt 512; $padCnt++) {  
  155.                 $PidLidFlagRequest = $PidLidFlagRequest + "00";  
  156.         }  
  157.         $stime = New-Object System.DateTime $StartTime.Year,$StartTime.Month,$StartTime.Day,0,0,0,0,Utc  
  158.         $dtime =  New-Object System.DateTime $DueTime.Year,$DueTime.Month,$DueTime.Day,0,0,0,0,Utc  
  159.         $etime = New-Object System.DateTime 1601, 1, 1, 0, 0, 0, 0,Utc  
  160.         [System.TimeSpan]$StartDateTimets = $stime - $etime  
  161.         [System.TimeSpan]$DateDueTimets = $dtime - $etime  
  162.         $HexDateStartTime = [System.Convert]::ToInt64($StartDateTimets.TotalMinutes).ToString("X8");  
  163.         $HexDateStartTime = $HexDateStartTime.Substring(6, 2) + $HexDateStartTime.Substring(4, 2) + $HexDateStartTime.Substring(2, 2) + $HexDateStartTime.Substring(0, 2);  
  164.         $HexDateDueTime = [System.Convert]::ToInt64($DateDueTimets.TotalMinutes).ToString("X8");  
  165.         $HexDateDueTime = $HexDateDueTime.Substring(6, 2) + $HexDateDueTime.Substring(4, 2) + $HexDateDueTime.Substring(2, 2) + $HexDateDueTime.Substring(0, 2);  
  166.         $ulVersion = "01000000";  
  167.         $dwFlags = "79000000"; #dwToDoItem,rtmStartDate,rtmDueDate ,wszFlagTo ,fReminderSet   
  168.         $dwToDoItem = $todoTimeFlagged;  
  169.         $wszFlagTo = $PidLidFlagRequest;  
  170.         $rtmStartDate = $HexDateStartTime;  
  171.         $rtmDueDate = $HexDateDueTime;  
  172.         $rtmReminder = "00000000";  
  173.         $fReminderSet = "00000000";  
  174.         return ($ulVersion + $dwFlags + $dwToDoItem + $wszFlagTo + $rtmStartDate + $rtmDueDate + $rtmReminder + $fReminderSet);  
  175.     }  
  176. }  
  177.  
  178. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  179. $SenderFlagEmail = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service    
  180. $SenderFlagEmail.ToRecipients.Add("glenscales@yahoo.com");  
  181. $SenderFlagEmail.Subject = "test";  
  182. $SenderFlagEmail.Body =  New-Object  Microsoft.Exchange.WebServices.Data.MessageBody([Microsoft.Exchange.WebServices.Data.BodyType]::HTML,"test");  
  183. $PR_SWAPPED_TODO_DATA = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0E2D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);  
  184. $PR_SWAPPED_TODO_STORE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0E2C,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);  
  185.   
  186. $adset = GetAutoDiscoverSettings -adEmailAddress $MailboxName -Credentials $creds  
  187. $storeID = ""  
  188. if($adset -is [Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverResponse]){  
  189.     Write-Host ("Get StoreId")  
  190.     $storeID = GetStoreId -AutoDiscoverSettings $adset  
  191. }  
  192.   
  193. $senderFlagHex = GetPidTagSwappedToDoData -FlagText $SenderFlagText -StartTime $FlagStartDate  -DueTime $FlagDueDate  
  194.   
  195. $SenderFlagEmail.SetExtendedProperty($PR_SWAPPED_TODO_DATA,(hex2binarray $senderFlagHex));  
  196. $SenderFlagEmail.SetExtendedProperty($PR_SWAPPED_TODO_STORE,(hex2binarray $storeID));  
  197. $SenderFlagEmail.SendAndSaveCopy();  
  198. Write-Host ("Message Sent")  


Create a Public Folder (or Mailbox folder) Post using EWS and Powershell

$
0
0
Somebody asked a couple of weeks ago about creating Post Items using EWS and Powershell which I haven't posted a sample for previously. Given its the first day of the year an example of a post about how to create a post to wish everybody happy new year seems like good idea.

Creating a POST item using the EWS Managed API is pretty straight forward as their is a PostItem Class you can use. To create a post is a folder you just need to know the ewsID of the folder you want to create the POST in. So in EWS you need some code that will either search and find the Folder in the Mailbox you want to post to or the Public Folder you want to post to.  I've created two sample one show how to create a post in Mailbox folders and the other in a Public folder. I've posted a download of the two sample here . The code for creating a POST in a Public folder looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72.   
  73. function FolderIdFromPath{  
  74.     param (  
  75.             $FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"  
  76.           )  
  77.     process{  
  78.         ## Find and Bind to Folder based on Path    
  79.         #Define the path to search should be seperated with \    
  80.         #Bind to the MSGFolder Root    
  81.         $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::PublicFoldersRoot)     
  82.         $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)    
  83.         #Split the Search path into an array    
  84.         $fldArray = $FolderPath.Split("\")  
  85.          #Loop through the Split Array and do a Search for each level of folder  
  86.         for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {  
  87.             #Perform search based on the displayname of each folder level  
  88.             $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)  
  89.             $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])  
  90.             $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)  
  91.             if ($findFolderResults.TotalCount -gt 0){  
  92.                 foreach($folder in $findFolderResults.Folders){  
  93.                     $tfTargetFolder = $folder                 
  94.                 }  
  95.             }  
  96.             else{  
  97.                 "Error Folder Not Found"   
  98.                 $tfTargetFolder = $null   
  99.                 break   
  100.             }      
  101.         }   
  102.         if($tfTargetFolder -ne $null){ 
  103.             return $tfTargetFolder.Id.UniqueId.ToString() 
  104.         } 
  105.     } 
  106. } 
  107. #Example use 
  108. $fldId = FolderIdFromPath -FolderPath "\aaaa\bbbb" 
  109. if($fldId -ne "Error Folder Not Found"){ 
  110.     $SubFolderId =  new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId) 
  111.     $SubFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId) 
  112.  
  113.     $NewPost = New-Object Microsoft.Exchange.WebServices.Data.PostItem -ArgumentList $service   
  114.     $NewPost.Subject = "Happy New Year"   
  115.     $NewPost.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody   
  116.     $NewPost.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML   
  117.     $NewPost.Body.Text = "Happy New Year" 
  118.     $NewPost.Save($SubFolder.Id)   
  119.     Write-Host ("Created Post")  
  120. }

Paging eDiscovery results with the EWS Managed API in Exchange 2013

$
0
0
eDiscovery is one of the new features in Exchange 2013 aimed at both improving the search experience and also helping deal with Big Data in a mailbox or mailboxes (which is kind of like the Sun in that its just keeps getting bigger and will one day consume us all).

With eDiscovery in EWS you can perform two types of searches a

  • Estimate Query - Which will return information about the number of hits for a particular KQL query.
  • Preview Query - Will return your query hits as PreviewItems which you can then use to show more information about each hit.

If your doing a Preview Query with a very generic search predicate that is going to be returning many preview Items and because of the size of these results they will returned as separate paged results sets. This will mean you will need to make multiple search requests to navigate thought the result set pages.

To tell the server you want the next Page in the Results set you need to use the PageItemReference  . The PageItemReference value needs to be set to the SortValue of the Last Preview-item returned by the previous page.

I've put together a couple of Managed API samples for this one is a C# example and the other is a Template Powershell script you can use to do a eDiscovery on one mailbox. These sample pages items back in lots of 100, you can adjust this value but I wouldn't go much over 1000.  I've put a download of the code here, the script looks like.

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $KQL = "Subject:test";  
  5. $SearchableMailboxString = $MailboxName;  
  6.   
  7. ## Load Managed API dll    
  8. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  9.     
  10. ## Set Exchange Version    
  11. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013    
  12.     
  13. ## Create Exchange Service Object    
  14. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  15.     
  16. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  17.     
  18. #Credentials Option 1 using UPN for the windows Account    
  19. $psCred = Get-Credential    
  20. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  21. $service.Credentials = $creds        
  22.     
  23. #Credentials Option 2    
  24. #service.UseDefaultCredentials = $true    
  25.     
  26. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  27.     
  28. ## Code From http://poshcode.org/624  
  29. ## Create a compilation environment  
  30. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  31. $Compiler=$Provider.CreateCompiler()  
  32. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  33. $Params.GenerateExecutable=$False  
  34. $Params.GenerateInMemory=$True  
  35. $Params.IncludeDebugInformation=$False  
  36. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  37.   
  38. $TASource=@' 
  39.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  40.     public class TrustAll : System.Net.ICertificatePolicy { 
  41.       public TrustAll() {  
  42.       } 
  43.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  44.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  45.         System.Net.WebRequest req, int problem) { 
  46.         return true; 
  47.       } 
  48.     } 
  49.   } 
  50. '@   
  51. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  52. $TAAssembly=$TAResults.CompiledAssembly  
  53.   
  54. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  55. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  56. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  57.   
  58. ## end code from http://poshcode.org/624  
  59.     
  60. ## 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    
  61.     
  62. #CAS URL Option 1 Autodiscover    
  63. $service.AutodiscoverUrl($MailboxName,{$true})    
  64. "Using CAS Server : " + $Service.url     
  65.      
  66. #CAS URL Option 2 Hardcoded    
  67.     
  68. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  69. #$service.Url = $uri      
  70.     
  71. ## Optional section for Exchange Impersonation    
  72.     
  73. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  74.   
  75.   
  76. $gsMBResponse = $service.GetSearchableMailboxes($SearchableMailboxString$false);  
  77. $gsMBResponse  
  78. $msbScope = New-Object  Microsoft.Exchange.WebServices.Data.MailboxSearchScope[] $gsMBResponse.SearchableMailboxes.Length  
  79. $mbCount = 0;  
  80. foreach ($sbMailbox in $gsMBResponse.SearchableMailboxes)  
  81. {  
  82.     $msbScope[$mbCount] = New-Object Microsoft.Exchange.WebServices.Data.MailboxSearchScope($sbMailbox.ReferenceId, [Microsoft.Exchange.WebServices.Data.MailboxSearchLocation]::All);  
  83.     $mbCount++;  
  84. }  
  85. $smSearchMailbox = New-Object Microsoft.Exchange.WebServices.Data.SearchMailboxesParameters  
  86. $mbq =  New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery($KQL$msbScope);  
  87. $mbqa = New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery[] 1  
  88. $mbqa[0] = $mbq  
  89. $smSearchMailbox.SearchQueries = $mbqa;  
  90. $smSearchMailbox.PageSize = 100;  
  91. $smSearchMailbox.PageDirection = [Microsoft.Exchange.WebServices.Data.SearchPageDirection]::Next;  
  92. $smSearchMailbox.PerformDeduplication = $false;             
  93. $smSearchMailbox.ResultType = [Microsoft.Exchange.WebServices.Data.SearchResultType]::PreviewOnly;  
  94. $srCol = $service.SearchMailboxes($smSearchMailbox);  
  95.   
  96. if ($srCol[0].Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success)  
  97. {  
  98.     if ($srCol[0].SearchResult.ItemCount -gt 0)  
  99.     {                    
  100.         do  
  101.         {  
  102.             $smSearchMailbox.PageItemReference = $srCol[0].SearchResult.PreviewItems[$srCol[0].SearchResult.PreviewItems.Length - 1].SortValue;  
  103.             foreach ($PvItem in $srCol[0].SearchResult.PreviewItems) {  
  104.                 Write-Host ($PvItem.Subject);  
  105.             }                          
  106.             $srCol = $service.SearchMailboxes($smSearchMailbox);  
  107.             Write-Host("Items Remaining : " + $srCol[0].SearchResult.ItemCount);  
  108.         } while ($srCol[0].SearchResult.ItemCount-gt 0 );  
  109.           
  110.     }  
  111.       
  112. }  


Accessing a User's Shared Contacts Folders in EWS

$
0
0
A while back I posted a sample of creating a Shortcut to a Shared Calendar folder using EWS here . In this post I'll look at how you can do the inverse which is reading and accessing the folders associated with these shortcuts. A users Shared Contacts folder's will appear like the following in Outlook


These Shortcuts are special items that are saved in the Common Views folder in the Non_IPM_Subtree of a mailbox. The data for the shortcut item is saved in a number of properties which are documented in the following Exchange protocol document http://msdn.microsoft.com/en-us/library/ee157359(v=exchg.80).aspx

So to access these shortcuts from EWS requires a few different operations, the first is you need to use a FindFolder operation on the Root of the Mailbox to located the CommonViews Folder. Once you have the CommonViews FolderId you then use the FindItems operation to find any of the Items where the PidTagWlinkGroupName is set to Shared Contacts, which will effectively filter the items returned just to the shared contacts (note at this point if you are using a localized version of Outlook you need to use the localized text name for the node).

Once you have access to the ShortCut Items the next step is to read the PidTagWlinkStoreEntryId property from the ShortCut item. To get the mailbox that this shortcut refers to you can extract the X500 address from StoreId format which is documented here . You can resolve the X500 Address to an SMTP address using the resolveName operation and then use the SMTP address bind to the shared Contacts folder normally in EWS.

I've posted a sample powershell script to access and query all the Shared Contact folders in a Mailbox here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.   
  71. #PropDefs   
  72. $pidTagStoreEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(4091, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  73. $PidTagNormalizedSubject = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0E1D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);   
  74. $PidTagWlinkType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6849, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  75. $PidTagWlinkFlags = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684A, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  76. $PidTagWlinkOrdinal = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684B, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  77. $PidTagWlinkFolderType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684F, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  78. $PidTagWlinkSection = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6852, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  79. $PidTagWlinkGroupHeaderID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6842, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  80. $PidTagWlinkSaveStamp = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6847, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  81. $PidTagWlinkGroupName = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6851, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)  
  82. $PidTagWlinkStoreEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684E, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  83. $PidTagWlinkGroupClsid = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6850, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  84. $PidTagWlinkEntryId = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684C, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  85. $PidTagWlinkRecordKey = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x684D, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  86. $PidTagWlinkCalendarColor = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6853, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  87. $PidTagWlinkAddressBookEID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6854,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  88. $PidTagWlinkROGroupType = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6892,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)  
  89. $PidTagWlinkAddressBookStoreEID = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6891,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)  
  90.   
  91.   
  92. $SharedFolders = @{}    
  93.     
  94. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  95. Write-Host ("Getting CommonVeiwFolder")  
  96. #Get CommonViewFolder  
  97. $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  98. $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)    
  99. $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)   
  100. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Common Views")   
  101. $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)   
  102. if ($findFolderResults.TotalCount -gt 0){   
  103.     $ExistingShortCut = $false  
  104.     $cvCommonViewsFolder = $findFolderResults.Folders[0]  
  105.     #Define ItemView to retrive just 1000 Items      
  106.     #Find Items that are unread  
  107.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  108.     $psPropset.add($PidTagWlinkStoreEntryId)  
  109.     $psPropset.add($PidTagWlinkFolderType)  
  110.     $cntSearch = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PidTagWlinkGroupName"Shared Contacts");  
  111.     $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)     
  112.     $ivItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated  
  113.     $ivItemView.PropertySet = $psPropset  
  114.     $fiItems = $service.FindItems($cvCommonViewsFolder.Id,$cntSearch,$ivItemView)      
  115.     foreach($Item in $fiItems.Items){  
  116.         $idVal = $null  
  117.         if($Item.TryGetProperty($PidTagWlinkStoreEntryId,[ref]$idVal)){  
  118.             Write-Host("Processing " + $Item.Subject)  
  119.                 $ssStoreID = $idVal;  
  120.                 $leLegDnStart = 0;  
  121.                 $lnLegDN = "";  
  122.                 for ($ssArraynum=($ssStoreID.Length - 2);$ssArraynum -ne 0; $ssArraynum--)  
  123.                         {  
  124.                             if ($ssStoreID[$ssArraynum] -eq 0)  
  125.                             {  
  126.                                 $leLegDnStart = $ssArraynum;  
  127.                                 $lnLegDN = [System.Text.ASCIIEncoding]::ASCII.GetString($ssStoreID$leLegDnStart + 1, ($ssStoreID.Length - ($leLegDnStart + 2)));  
  128.                                 $ssArraynum = 1;  
  129.                             }  
  130.                         }  
  131.                         Write-Host($lnLegDN)  
  132.                         $ncCol = $service.ResolveName($lnLegDN, [Microsoft.Exchange.WebServices.Data.ResolveNameSearchLocation]::DirectoryOnly, $true);  
  133.                         if ($ncCol.Count -gt 0)  
  134.                         {  
  135.                             try  
  136.                             {  
  137.                                 $SharedContactsId = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts, $ncCol[0].Mailbox.Address);  
  138.                                 $SharedContactFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service$SharedContactsId);  
  139.                                 $SharedFolders.Add($ncCol[0].Mailbox.Address, $SharedContactFolder);  
  140.                             }  
  141.                             catch  {  
  142.                                 Write-Host "Error getting Shared Folder"  
  143.                             }  
  144.                         }  
  145.               
  146.         }                               
  147.     }  
  148. }  
  149. if($SharedFolders.Keys.Count -ne 0){  
  150.     foreach($mbFolder in $SharedFolders.Keys){  
  151.         #Define ItemView to retrive just 1000 Items      
  152.         $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)      
  153.         $fiItems = $null      
  154.         do{      
  155.             $fiItems = $service.FindItems($SharedFolders[$mbFolder].Id,$ivItemView)      
  156.             #[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  157.             foreach($Item in $fiItems.Items){        
  158.                 Write-Host ("Mailbox : " + $mbFolder)  
  159.                 Write-Host ("Contact : " + $Item.Subject + " : " + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1])  
  160.             }      
  161.             $ivItemView.Offset += $fiItems.Items.Count      
  162.         }while($fiItems.MoreAvailable -eq $true)   
  163.     }  
  164. }  


MEC is back again this year

$
0
0
MEC the Microsoft Exchange Conference is on again this year in April and is shaping up to be a not to be missed event if your interested in Exchange. Unlike other conferences MEC doesn't come around every year which adds to the charm and freshness of the content you can expect to hear. Also the Exchange community are pretty awesome so it is not just the technology that makes this event but rather the people you can meet and listen to.

If your a developer or interested in coding on Exchange there is an Exchange Extensibility track http://www.mecisback.com/Sessions.aspx which covers among other things some of the new things you can do with Exchange 2013 and Exchange Online.  As with many of the sessions at MEC a lot these are being delivered by the people at Microsoft who are responsible for designing and delivering these features, so its one of the only places you can get those hard questions answered or put some feedback forward. The future look sessions also look pretty interesting to get glimpse of the near future.

I'm also really excited about the session I'll be giving at MEC on "Using Exchange as a platform for Innovation"http://www.mecisback.com/SessionDetail.aspx?id=14161 . My session content will be a bit out of the box and different, it might be a bit of an 11 on the technical side as i have a pretty broad scope to cover, I'll be talking on both hardware and software and how you can build Mailbox centric solutions on unconventional devices. So if your interested in what you can do with a Raspberry Pi connecting to an Exchange Mailbox with EWS or interesting in learning how the Maker movement may start interfacing with Exchange then this is the session for you. I hope to have a least 3 unique prototypes to show and a little bit of usually blog type scripts at the end.

You can get all the other details on MEC at http://www.mecisback.com/


Adding an additional/Shared Mailbox to OWA with EWS and Powershell in Exchange 2013

$
0
0
The ability to add a shared Mailbox Folders was a feature that was introduced in Exchange 2010 and carried over into 2013. When an additional Mailbox folder is added this updates an XML configuration documented in the OWA.OtherMailbox FAI Item for this Mailbox.

In Exchange 2010 this includes an Id to the Folder but in Exchange2013/OWA it just includes the PrimarySMTP of the Mailbox your adding while the folderId is left blank. Due to this reason this script won't work on a Exchange 2010 server.

Note : Its important to point out even on 2013/Office365 this type of script that modifies the configuration item would also not be supported as it may inadvertently corrupt the config document so it's provided as is for testing only.

 The script to do this looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $maMailboxToAdd = $args[1]  
  5.   
  6. ## Load Managed API dll    
  7. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  8.     
  9. ## Set Exchange Version    
  10. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013  
  11.     
  12. ## Create Exchange Service Object    
  13. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  14.     
  15. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  16.     
  17. #Credentials Option 1 using UPN for the windows Account    
  18. $psCred = Get-Credential    
  19. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  20. $service.Credentials = $creds        
  21.     
  22. #Credentials Option 2    
  23. #service.UseDefaultCredentials = $true    
  24.     
  25. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  26.     
  27. ## Code From http://poshcode.org/624  
  28. ## Create a compilation environment  
  29. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  30. $Compiler=$Provider.CreateCompiler()  
  31. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  32. $Params.GenerateExecutable=$False  
  33. $Params.GenerateInMemory=$True  
  34. $Params.IncludeDebugInformation=$False  
  35. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  36.   
  37. $TASource=@' 
  38.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  39.     public class TrustAll : System.Net.ICertificatePolicy { 
  40.       public TrustAll() {  
  41.       } 
  42.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  43.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  44.         System.Net.WebRequest req, int problem) { 
  45.         return true; 
  46.       } 
  47.     } 
  48.   } 
  49. '@   
  50. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  51. $TAAssembly=$TAResults.CompiledAssembly  
  52.   
  53. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  54. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  55. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  56.   
  57. ## end code from http://poshcode.org/624  
  58.     
  59. ## 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    
  60.     
  61. #CAS URL Option 1 Autodiscover    
  62. $service.AutodiscoverUrl($MailboxName,{$true})    
  63. "Using CAS Server : " + $Service.url     
  64.      
  65. #CAS URL Option 2 Hardcoded    
  66.     
  67. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  68. #$service.Url = $uri      
  69.     
  70. ## Optional section for Exchange Impersonation    
  71.     
  72. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  73.   
  74. $exists = $false;  
  75. $updated = $false;  
  76. # Bind to the MsgFolderRoot folder    
  77. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)     
  78. $Root = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  79. #Check for existing Item  
  80. $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,"IPM.Configuration.OWA.OtherMailbox")  
  81. $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1)  
  82. $ivItemView.Traversal =  [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Associated  
  83. $fiResults = $service.FindItems($Root.Id,$SfSearchFilter,$ivItemView)  
  84. if($fiResults.Items.Count -eq 0){  
  85.     Write-Host ("No Config Item found, create new Item")  
  86.     $UMConfig = New-Object Microsoft.Exchange.WebServices.Data.UserConfiguration -ArgumentList $service  
  87.     $UMConfig.Save("OWA.OtherMailbox",$Root.Id)  
  88. }  
  89. else{  
  90.     Write-Host ("Existing Config Item Found");  
  91. }  
  92. $owaOtherMailbox = [Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($service"OWA.OtherMailbox"$Root.Id, [Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All);  
  93. #Make sure the server is running 2013 this script doesn't work on Exchange 2010  
  94. if($service.service.ServerInfo.MajorVersion -ge 15){  
  95.     $xmlConfig = New-Object System.Xml.XmlDocument  
  96.     if ($owaOtherMailbox.XmlData -eq $null)  
  97.     {  
  98.        $xmlConfig.LoadXml("<OtherMailbox></OtherMailbox>");  
  99.     }  
  100.     else  
  101.      {  
  102.        $xmlConfig.LoadXml([System.Text.UTF8Encoding]::UTF8.GetString($owaOtherMailbox.XmlData));  
  103.     }  
  104.     if($xmlConfig.OtherMailbox.entry -ne $null){  
  105.         foreach($obMailbox in  $xmlConfig.OtherMailbox.entry){  
  106.             Write-host ("Processing Mailbox : " + $obMailbox.principalSMTPAddress);  
  107.             if($obMailbox.principalSMTPAddress -eq $maMailboxToAdd.ToLower()){  
  108.                 $exists = $true;  
  109.             }  
  110.         }  
  111.     }  
  112.     if (!$exists)  
  113.     {  
  114.       $ncCol = $service.ResolveName($maMailboxToAdd,[Microsoft.Exchange.WebServices.Data.ResolveNameSearchLocation]::DirectoryOnly,$true);  
  115.       if ($ncCol.Count -gt 0)  
  116.        {  
  117.             $newmb = $xmlConfig.CreateElement("entry");  
  118.             $newmb.SetAttribute("displayName"$ncCol[0].Contact.DisplayName);  
  119.             $newmb.SetAttribute("rootFolderId""");  
  120.             $newmb.SetAttribute("principalSMTPAddress"$ncCol[0].Mailbox.Address);  
  121.             $newmb.InnerText = "";  
  122.             $xmlConfig.DocumentElement.AppendChild($newmb);  
  123.             $updated = $true;  
  124.        }  
  125.     }  
  126.     else  
  127.     {  
  128.         write-host ("Mailbox already added");  
  129.     }                 
  130.   
  131.     if ($updated) {  
  132.         $xmlbytes = [System.Text.UTF8Encoding]::UTF8.GetBytes($xmlConfig.OuterXml);  
  133.         $owaOtherMailbox.XmlData = $xmlbytes;  
  134.         $owaOtherMailbox.Update();  
  135.         write-host ("Config updated")  
  136.     }  
  137. }  



Exporting Contacts to a CSV file using the EWS Managed API and Powershell

$
0
0
Somebody asked last week about exporting contacts from Exchange via EWS to a CSV file and I realised I didn't have a basic sample for doing this. Contacts are one of the more richer exchange datatypes and can hold a lot of different information which you may or may not want to capture in a CSV export. In this sample script I'll show you how you can export data from the normal contact strongly typed properties like GivenName and Surname and the Indexed properties which are used to store the EmailAddresses, PhoneNumbers and Address details and also any extended properties like the Gender property which there are no strongly typed property for.

As I mentioned Contacts have a lot of properties so this script doesn't export everything just a subsection to show how to export properties from each of the different subgroups I talked about. To add other properties to script eg  like the JobTitle you need to make the following modifications

Add the property to the Custom object

$expObj = "" | select DisplayName,GivenName,Surname,Gender,Email1DisplayName,Email1Type,Email1EmailAddress,BusinessPhone,MobilePhone,HomePhone,BusinessStreet,BusinessCity,BusinessState,HomeStreet,HomeCity,HomeState,JobTitle

Then set the property within the Item Iteration

$expObj.JobTitle  = $item.JobTitle

I've put a download of this script here the code looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2  
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## 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    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72. $ExportCollection = @()  
  73. Write-Host "Process Contacts"  
  74. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts,$MailboxName)     
  75. $Contacts = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  76.   
  77. $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)   
  78. $PR_Gender = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14925,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Short)  
  79. $psPropset.Add($PR_Gender)  
  80.    
  81. #Define ItemView to retrive just 1000 Items      
  82. $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)      
  83. $fiItems = $null      
  84. do{      
  85.     $fiItems = $service.FindItems($Contacts.Id,$ivItemView)   
  86.     [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  87.     foreach($Item in $fiItems.Items){       
  88.         if($Item -is [Microsoft.Exchange.WebServices.Data.Contact]){  
  89.             $expObj = "" | select DisplayName,GivenName,Surname,Gender,Email1DisplayName,Email1Type,Email1EmailAddress,BusinessPhone,MobilePhone,HomePhone,BusinessStreet,BusinessCity,BusinessState,HomeStreet,HomeCity,HomeState  
  90.             $expObj.DisplayName = $Item.DisplayName  
  91.             $expObj.GivenName = $Item.GivenName  
  92.             $expObj.Surname = $Item.Surname  
  93.             $expObj.Gender = ""  
  94.             $Gender = $null  
  95.             if($item.TryGetProperty($PR_Gender,[ref]$Gender)){  
  96.                 if($Gender -eq 2){  
  97.                     $expObj.Gender = "Male"   
  98.                 }  
  99.                 if($Gender -eq 1){  
  100.                     $expObj.Gender = "Female"   
  101.                 }  
  102.             }  
  103.             $BusinessPhone = $null  
  104.             $MobilePhone = $null  
  105.             $HomePhone = $null  
  106.             if($Item.PhoneNumbers -ne $null){  
  107.                 if($Item.PhoneNumbers.TryGetValue([Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::BusinessPhone,[ref]$BusinessPhone)){  
  108.                     $expObj.BusinessPhone = $BusinessPhone  
  109.                 }  
  110.                 if($Item.PhoneNumbers.TryGetValue([Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::MobilePhone,[ref]$MobilePhone)){  
  111.                     $expObj.MobilePhone = $MobilePhone  
  112.                 }     
  113.                 if($Item.PhoneNumbers.TryGetValue([Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::HomePhone,[ref]$HomePhone)){  
  114.                     $expObj.HomePhone = $HomePhone  
  115.                 }     
  116.             }             
  117.             if($Item.EmailAddresses.Contains([Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1)){                  
  118.                 $expObj.Email1DisplayName = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Name  
  119.                 $expObj.Email1Type = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].RoutingType  
  120.                 $expObj.Email1EmailAddress = $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address  
  121.             }  
  122.             $HomeAddress = $null  
  123.             $BusinessAddress = $null  
  124.             if($item.PhysicalAddresses -ne $null){  
  125.                 if($item.PhysicalAddresses.TryGetValue([Microsoft.Exchange.WebServices.Data.PhysicalAddressKey]::Home,[ref]$HomeAddress)){  
  126.                     $expObj.HomeStreet = $HomeAddress.Street  
  127.                     $expObj.HomeCity = $HomeAddress.City  
  128.                     $expObj.HomeState = $HomeAddress.State  
  129.                 }  
  130.                 if($item.PhysicalAddresses.TryGetValue([Microsoft.Exchange.WebServices.Data.PhysicalAddressKey]::Business,[ref]$BusinessAddress)){  
  131.                     $expObj.BusinessStreet = $BusinessAddress.Street  
  132.                     $expObj.BusinessCity = $BusinessAddress.City  
  133.                     $expObj.BusinessState = $BusinessAddress.State  
  134.                 }  
  135.             }  
  136.               
  137.             $ExportCollection += $expObj  
  138.         }  
  139.     }      
  140.     $ivItemView.Offset += $fiItems.Items.Count      
  141. }while($fiItems.MoreAvailable -eq $true)   
  142.   
  143. $fnFileName = "c:\temp\" + $MailboxName + "-ContactsExport.csv" 
  144. $ExportCollection | Export-Csv -NoTypeInformation -Path $fnFileName 
  145. "Exported to " + $fnFileName  

Looking for your questions on Exchange extensibility for MEC

$
0
0
This blog has been a little quiet over the last month as I've been putting all my efforts into my MEC Talk which is now only a week away, but I should have a number of new posts soon after that.

In addition to this talk I am going to be moderating the Experts Unplugged: Exchange Extensibility session at MEC. If your interested in Exchange extensibility in any way this should be a really good session to attend, as we will be able to discuss exchange extensibility as a whole across EWS, Transport, Apps and Powershell and not just the individual components. The Panel for this session is made up of Subject matter experts from Microsoft who are responsible for designing,building, and directing the future of extensibility in Exchange. So this will be great opportunity for the community to come together and discuss where we are at with extensibility, some of new exciting stuff in SP1 and where we are going in the future. More importantly its your opportunity if you are attending MEC to give some feedback to the panel.

If you aren't able to get to MEC or you can't get to this session and you have a really good question (or piece of feedback) or something you would really like to see in Exchange extensibility. Please Email me glenscales@yahoo.com or post your question as a comment on this blog entry and I'll try to include this in the discussion and get back to you with an answer of some sort. If you are going to attend this session and are a bit shy about asking questions you can also let me know your questions.

If you haven't heard already Exchange 2013 SP1 both (onPrem and Online) has been released bearing extensibility gifts, here are some pre reading links for some of the new SP1 bits and new Exchange Online bits that are worth reading on your way to MEC.

Compose Apps
EWS what's new in 2013 SP1 (and the new EWS Managed API 2.1)
(Exchange On-line and the new OData API)
Authentication and authorization using Common Consent Framework







Export the GAL or Address list with EWS to Vcards in 2013 MEC sample 1

$
0
0
This is the first of the sample scripts I included in my MEC talk last Monday.  This script uses 3 of the new operations in EWS in Exchange 2013 to export all the address list entries of a particular Addresslist to individual Vcards (including the user photo) using just EWS. None of these operations are included in the EWS Managed API, so to use these operations I'm using some Raw SOAP and posting the EWS request using the standard httpwebrequest class in .Net and processing the response in XML.

The FindPeople Operation is used firstly to page through all the entries in an Address list in groups of 100 entries at a time. To use this operation you first need to get the GUID for the address list you want to export and hard code this in the $ABGUID variable eg.

$ABGUID = "6c118670-2f72-4213-944c-ab1e97d63f9b";

To get the GUID for your Global Address List you need to use the Get-GlobalAddressList Cmdlet http://technet.microsoft.com/en-us/library/aa996579%28v=exchg.150%29.aspx or if you want to export one of the other Address lists you can use the Get-AddressList cmdlet http://technet.microsoft.com/en-us/library/aa996782(v=exchg.150).aspx .

eg



To use these cmdlet's in Office365 you need to make sure you are assigned the Address Lists Role in RBAC

eg run

New-ManagementRoleAssignment -Role 'Address Lists' -User username

The FindPeople operation returns the PersonaId for each of the AddressList entries so the next part of the script uses the GetPersona Operation to get all the Address list entries details. Personas are a different way of representing contact data this is outlined in http://msdn.microsoft.com/en-us/library/office/jj190895(v=exchg.150).aspx . This particular script just uses this operation to grab the contact details stored in Active Directory (or Azure AD) for the particular persona (or Address list Entry in question). Currently I haven't worked out a way to batch this operation in EWS (if anyone knows please ping me) so its executing one OP per entry(which is kind of slow).
The last operation the script uses is the GerUserPhoto operation which is just a REST endpoint so it's nice and easy to use. It returns a ByteArray which then gets converted to a Base64 string with LineBreaks so its properly formatted in the VCard file.
To run this script you need to pass it in a Mailbox to run as and as I mentioned before you need to hardcode the GUID of the AddressList you want to export. While none of the operations used to do the export are in the Managed API, I've included in the usually Managed API Autodiscovery routines.  You can also set the location of the export directory in 
$exportFolder = "c:\temp\"
I've put a download of this script here the code itself looks like


  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $exportFolder = "c:\temp\" 
  5.  
  6. $ABGUID = "6c118670-2f72-4213-944c-ab1e97d63f9b"; 
  7.  
  8. ## Load Managed API dll   
  9. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"   
  10.    
  11. ## Set Exchange Version   
  12. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2   
  13.    
  14. ## Create Exchange Service Object   
  15. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)   
  16.    
  17. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials   
  18.    
  19. #Credentials Option 1 using UPN for the windows Account   
  20. $psCred = Get-Credential   
  21. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())   
  22. $service.Credentials = $creds       
  23.    
  24. #Credentials Option 2   
  25. #service.UseDefaultCredentials = $true   
  26.    
  27. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates   
  28.    
  29. ## Code From http://poshcode.org/624 
  30. ## Create a compilation environment 
  31. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider 
  32. $Compiler=$Provider.CreateCompiler() 
  33. $Params=New-Object System.CodeDom.Compiler.CompilerParameters 
  34. $Params.GenerateExecutable=$False 
  35. $Params.GenerateInMemory=$True 
  36. $Params.IncludeDebugInformation=$False 
  37. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null 
  38.  
  39. $TASource=@' 
  40.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  41.     public class TrustAll : System.Net.ICertificatePolicy { 
  42.       public TrustAll() {  
  43.       } 
  44.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  45.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  46.         System.Net.WebRequest req, int problem) { 
  47.         return true; 
  48.       } 
  49.     } 
  50.   } 
  51. '@  
  52. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) 
  53. $TAAssembly=$TAResults.CompiledAssembly 
  54.  
  55. ## We now create an instance of the TrustAll and attach it to the ServicePointManager 
  56. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") 
  57. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll 
  58.  
  59. ## end code from http://poshcode.org/624 
  60.    
  61. ## 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   
  62.    
  63. #CAS URL Option 1 Autodiscover   
  64. $service.AutodiscoverUrl($MailboxName,{$true})   
  65. "Using CAS Server : " + $Service.url    
  66.  
  67.  
  68.     
  69. #CAS URL Option 2 Hardcoded   
  70.    
  71. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"   
  72. #$service.Url = $uri     
  73.    
  74. ## Optional section for Exchange Impersonation   
  75.    
  76. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)  
  77.  
  78. function getPeopleRequest($offset){ 
  79.  
  80. $request = @"  
  81. <?xml version="1.0" encoding="utf-8"?>  
  82. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  83. <soap:Header>  
  84. <RequestServerVersion Version="Exchange2013" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />  
  85. </soap:Header><soap:Body>  
  86. <FindPeople xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"><PersonaShape>  
  87. <BaseShape xmlns="http://schemas.microsoft.com/exchange/services/2006/types">Default</BaseShape>  
  88. </PersonaShape><IndexedPageItemView MaxEntriesReturned="100" Offset="$offset" BasePoint="Beginning" />  
  89. <ParentFolderId>  
  90. <AddressListId Id="$ABGUID" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />  
  91. </ParentFolderId></FindPeople></soap:Body></soap:Envelope>  
  92. "@ 
  93. return $request 
  94. } 
  95.  
  96. function getPersonaRequest($PersonalId){ 
  97. $request = @"  
  98. <?xml version="1.0" encoding="utf-8"?>  
  99. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"  
  100.                xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">  
  101.   <soap:Header>  
  102.     <t:RequestServerVersion Version="Exchange2013"/>  
  103.   </soap:Header>  
  104.   <soap:Body xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">  
  105.     <GetPersona>  
  106.       <PersonaId Id="$PersonalId"/>  
  107.     </GetPersona>  
  108.   </soap:Body>  
  109. </soap:Envelope>  
  110. "@ 
  111. return $request 
  112. } 
  113.  
  114. function AutoDiscoverPhotoURL{ 
  115.        param ( 
  116.               $EmailAddress="$( throw 'Email is a mandatory Parameter' )", 
  117.               $Credentials="$( throw 'Credentials is a mandatory Parameter' )" 
  118.               ) 
  119.        process{ 
  120.               $version= [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013 
  121.               $adService= New-Object Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverService($version); 
  122.               $adService.Credentials = $Credentials 
  123.               $adService.EnableScpLookup=$false; 
  124.               $adService.RedirectionUrlValidationCallback= {$true} 
  125.               $adService.PreAuthenticate=$true; 
  126.               $UserSettings= new-object Microsoft.Exchange.WebServices.Autodiscover.UserSettingName[] 1 
  127.               $UserSettings[0] = [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalPhotosUrl 
  128.               $adResponse=$adService.GetUserSettings($EmailAddress, $UserSettings) 
  129.               $PhotoURI= $adResponse.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalPhotosUrl] 
  130.               return $PhotoURI.ToString() 
  131.        } 
  132. } 
  133.  
  134. $Script:PhotoURL = AutoDiscoverPhotoURL -EmailAddress $MailboxName  -Credentials $creds 
  135. Write-host ("Photo URL : " + $Script:PhotoURL)  
  136.  
  137. function ProcessPersona($PersonalId){ 
  138.  
  139.  
  140.     $personalRequest = getPersonaRequest ($PersonalId) 
  141.     $mbMailboxFolderURI = New-Object System.Uri($service.url)   
  142.     $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)   
  143.     $wrWebRequest.CookieContainer =  New-Object System.Net.CookieContainer    
  144.     $wrWebRequest.KeepAlive = $false;   
  145.     $wrWebRequest.Headers.Set("Pragma", "no-cache");   
  146.     $wrWebRequest.Headers.Set("Translate", "f");   
  147.     $wrWebRequest.Headers.Set("Depth", "0");   
  148.     $wrWebRequest.ContentType = "text/xml";   
  149.     $wrWebRequest.ContentLength = $expRequest.Length;   
  150.     $wrWebRequest.Timeout = 90000;   
  151.     $wrWebRequest.Method = "POST";   
  152.     $wrWebRequest.Credentials = $creds   
  153.     $wrWebRequest.UserAgent = "EWS Script" 
  154.      
  155.     $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($personalRequest);   
  156.     $wrWebRequest.ContentLength = $bqByteQuery.Length;   
  157.     $rsRequestStream = $wrWebRequest.GetRequestStream();   
  158.     $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);   
  159.     $rsRequestStream.Close();   
  160.     $wrWebResponse = $wrWebRequest.GetResponse();   
  161.     $rsResponseStream = $wrWebResponse.GetResponseStream()   
  162.     $sr = new-object System.IO.StreamReader($rsResponseStream);   
  163.     $rdResponseDocument = New-Object System.Xml.XmlDocument   
  164.     $rdResponseDocument.LoadXml($sr.ReadToEnd());   
  165.     $Persona =@($rdResponseDocument.getElementsByTagName("Persona"))  
  166.     $Persona 
  167.     $DisplayName = ""; 
  168.     if($Persona.DisplayName -ne $null){ 
  169.         $DisplayName = $Persona.DisplayName."#text"  
  170.     }   
  171.     $fileName =  $exportFolder + $DisplayName + "-" + [Guid]::NewGuid().ToString() + ".vcf" 
  172.     add-content -path $filename "BEGIN:VCARD" 
  173.     add-content -path $filename "VERSION:2.1" 
  174.     $givenName = "" 
  175.     if($Persona.GivenName -ne $null){ 
  176.         $givenName = $Persona.GivenName."#text"  
  177.     }  
  178.     $surname = "" 
  179.     if($Persona.Surname -ne $null){ 
  180.         $surname = $Persona.Surname."#text"  
  181.     }  
  182.     add-content -path $filename ("N:" + $surname + ";" + $givenName) 
  183.     add-content -path $filename ("FN:" + $Persona.DisplayName."#text")  
  184.     $Department = ""; 
  185.     if($Persona.Department -ne $null){ 
  186.         $Department = $Persona.Department."#text"  
  187.     }  
  188.     if($Persona.EmailAddress -ne $null){  
  189.         add-content -path $filename ("EMAIL;PREF;INTERNET:" + $Persona.EmailAddress.EmailAddress) 
  190.     } 
  191.     $CompanyName = ""; 
  192.     if($Persona.CompanyName -ne $null){ 
  193.         $CompanyName = $Persona.CompanyName."#text"  
  194.     }  
  195.     add-content -path $filename ("ORG:" + $CompanyName + ";" + $Department)  
  196.     if($Persona.Titles -ne $null){ 
  197.         add-content -path $filename ("TITLE:" + $Persona.Titles.StringAttributedValue.Value) 
  198.     } 
  199.     if($Persona.MobilePhones -ne $null){ 
  200.         add-content -path $filename ("TEL;CELL;VOICE:" + $Persona.MobilePhones.PhoneNumberAttributedValue.Value.Number)      
  201.     } 
  202.     if($Persona.HomePhones -ne $null){ 
  203.         add-content -path $filename ("TEL;HOME;VOICE:" + $Persona.HomePhones.PhoneNumberAttributedValue.Value.Number)        
  204.     } 
  205.     if($Persona.BusinessPhoneNumbers -ne $null){ 
  206.         add-content -path $filename ("TEL;WORK;VOICE:" + $Persona.BusinessPhoneNumbers.PhoneNumberAttributedValue.Value.Number)      
  207.     } 
  208.     if($Persona.WorkFaxes -ne $null){ 
  209.         add-content -path $filename ("TEL;WORK;FAX:" + $Persona.WorkFaxes.PhoneNumberAttributedValue.Value.Number) 
  210.     } 
  211.     if($Persona.BusinessHomePages -ne $null){ 
  212.         add-content -path $filename ("URL;WORK:" + $Persona.BusinessHomePages.StringAttributedValue.Value) 
  213.     } 
  214.     if($Persona.BusinessAddresses -ne $null){ 
  215.         $Country = $Persona.BusinessAddresses.PostalAddressAttributedValue.Value.Country 
  216.         $City = $Persona.BusinessAddresses.PostalAddressAttributedValue.Value.City 
  217.         $Street = $Persona.BusinessAddresses.PostalAddressAttributedValue.Value.Street 
  218.         $State = $Persona.BusinessAddresses.PostalAddressAttributedValue.Value.State 
  219.         $PCode = $Persona.BusinessAddresses.PostalAddressAttributedValue.Value.PostalCode 
  220.         $addr =  "ADR;WORK;PREF:;" + $Country + ";" + $Street + ";" +$City + ";" + $State + ";" + $PCode + ";" + $Country 
  221.         add-content -path $filename $addr 
  222.     } 
  223.     try{ 
  224.         $PhotoSize = "HR96x96 
  225.         $PhotoURL= $Script:PhotoURL + "/GetUserPhoto?email="  + $Persona.EmailAddress.EmailAddress + "&size=" + $PhotoSize; 
  226.         $wbClient = new-object System.Net.WebClient 
  227.         $wbClient.Credentials = $creds 
  228.         $photoBytes = $wbClient.DownloadData($PhotoURL); 
  229.         add-content -path $filename "PHOTO;ENCODING=BASE64;TYPE=JPEG:" 
  230.         $ImageString = [System.Convert]::ToBase64String($photoBytes,[System.Base64FormattingOptions]::InsertLineBreaks) 
  231.         add-content -path $filename $ImageString 
  232.         add-content -path $filename "`r`n"   
  233.     } 
  234.     catch{ 
  235.  
  236.     } 
  237.     add-content -path $filename "END:VCARD" 
  238.  
  239. } 
  240.  
  241. $peopleCollection = @() 
  242. $offset = 0; 
  243.  
  244. do{ 
  245.     $mbMailboxFolderURI = New-Object System.Uri($service.url)   
  246.     $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)   
  247.     $wrWebRequest.CookieContainer =  New-Object System.Net.CookieContainer    
  248.     $wrWebRequest.KeepAlive = $false;   
  249.     $wrWebRequest.Useragent = "EWS Script" 
  250.     $wrWebRequest.Headers.Set("Pragma", "no-cache");   
  251.     $wrWebRequest.Headers.Set("Translate", "f");   
  252.     $wrWebRequest.Headers.Set("Depth", "0");   
  253.     $wrWebRequest.ContentType = "text/xml";   
  254.     $wrWebRequest.ContentLength = $expRequest.Length;   
  255.     $wrWebRequest.Timeout = 60000;   
  256.     $wrWebRequest.Method = "POST";   
  257.     $wrWebRequest.Credentials = $creds   
  258.  
  259.     $fpRequest = getPeopleRequest ($offset) 
  260.     $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($fpRequest);   
  261.     $wrWebRequest.ContentLength = $bqByteQuery.Length;   
  262.     $rsRequestStream = $wrWebRequest.GetRequestStream();   
  263.     $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);   
  264.     $rsRequestStream.Close();   
  265.     $wrWebResponse = $wrWebRequest.GetResponse();   
  266.     $rsResponseStream = $wrWebResponse.GetResponseStream()   
  267.     $sr = new-object System.IO.StreamReader($rsResponseStream);   
  268.     $rdResponseDocument = New-Object System.Xml.XmlDocument   
  269.     $rdResponseDocument.LoadXml($sr.ReadToEnd());   
  270.     $totalCount = @($rdResponseDocument.getElementsByTagName("TotalNumberOfPeopleInView"))  
  271.     $Personas =@($rdResponseDocument.getElementsByTagName("Persona"))   
  272.     Write-Host ("People Count : " + $Personas.Count) 
  273.     $offset += $Personas.Count 
  274.     foreach($persona in $Personas){ 
  275.         if($persona.PersonaType -eq "Person"){ 
  276.             ProcessPersona($persona.PersonaId.Id.ToString())  
  277.         } 
  278.     } 
  279.     [Int32]$tc = $totalCount."#text"  
  280.     Write-Host ("Offset: " + $offset) 
  281.     Write-Host ("Total count: " + $tc)  
  282.   
  283. }while($tc -gt $offset)  

Pull Subscription Mailbox Item move tracking script MEC sample 2

$
0
0
This is the second of the Powershell script samples from my MEC talk last week. The idea behind this script is to track the movement of Items in a Mailbox between folders using Pull Notifications in EWS, you can read more about how Pull notifications work here .  This script takes advantage of the fact that when you enable Single Item Recovery any deletes you make in a Mailbox (hard or soft) the Items aren't deleted rather moved to the Dumpster v2 RecoverableItems folders. So if you track all the move notification events you can track both the movement of Messages by rules, users or deletes. eg the results look like



I've created two different versions of this script, the first version subscribes to all folders in a Mailbox and runs continuously against the Mailbox and does a GetEvents request every minute to retrieve the latest events from all folders in a Mailbox and then logs that to file.

The second version just subscribes to the Inbox folder and saves the subscription information out to file when you run it the first time. Then the next time you run the script it will use the saved SubscriptionId and Watermark value to get all the events since the script was last run. Pull Subscriptions have a timeout of 1440 minutes (which is 1 day) so if you are going to run this version you need to run it with a maximum gap of 1 day. The idea of this second version was just to track inbox fan-out of messages rather then subscribing to all folders like the first version.

Both of these scripts use EWS Impersonation which must be configured for the account your going to run the script as. When you run the script you need to pass in the Mailbox to run against and the timeout value in minutes for subscription which needs to between 1 and 1440 eg

./pullSubTrackWm.ps1 jcool@msgdevelop.onmicrosoft.com 1440

I've put a download of both scripts here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2. $MailboxName = $args[0]  
  3. $duration = $args[1]  
  4. $sw = [system.diagnostics.stopwatch]::startNew()  
  5. $Error.Clear()  
  6. $Script:changeLog = "c:\temp\ChangeLog-$(get-date -f yyyy-MM-dd).csv";   
  7. if(-Not(Test-Path -Path $Script:changeLog)){  
  8.     Add-Content -Path $Script:changeLog ("EventTime,MessageDateTimeReceived,Subject,MovedFrom,MovedTo,LastModifiedName")  
  9. }  
  10. #Add-Content -Path $Script:changeLog ("Start Log" + (Get-Date).ToString())   
  11.   
  12. ## Load Managed API dll    
  13. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  14. Add-Type -AssemblyName System.Runtime.Serialization  
  15. ## Set Exchange Version    
  16. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  17.     
  18. ## Create Exchange Service Object    
  19. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  20.     
  21. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  22.     
  23. #Credentials Option 1 using UPN for the windows Account    
  24. $psCred = Get-Credential    
  25. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  26. $service.Credentials = $creds        
  27.     
  28. #Credentials Option 2    
  29. #service.UseDefaultCredentials = $true    
  30.     
  31. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  32.     
  33. ## Code From http://poshcode.org/624  
  34. ## Create a compilation environment  
  35. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  36. $Compiler=$Provider.CreateCompiler()  
  37. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  38. $Params.GenerateExecutable=$False  
  39. $Params.GenerateInMemory=$True  
  40. $Params.IncludeDebugInformation=$False  
  41. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  42.   
  43. $TASource=@' 
  44.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  45.     public class TrustAll : System.Net.ICertificatePolicy { 
  46.       public TrustAll() {  
  47.       } 
  48.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  49.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  50.         System.Net.WebRequest req, int problem) { 
  51.         return true; 
  52.       } 
  53.     } 
  54.   } 
  55. '@   
  56. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  57. $TAAssembly=$TAResults.CompiledAssembly  
  58.   
  59. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  60. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  61. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  62.   
  63. ## end code from http://poshcode.org/624  
  64.     
  65. ## 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    
  66.     
  67. #CAS URL Option 1 Autodiscover    
  68. $service.AutodiscoverUrl($MailboxName,{$true})    
  69. "Using CAS Server : " + $Service.url     
  70.   
  71.   
  72. #CAS URL Option 2 Hardcoded    
  73.     
  74. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  75. #$service.Url = $uri      
  76.     
  77. ## Optional section for Exchange Impersonation    
  78.     
  79. $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  80. #check Anchor header for Exchange 2013/Office365    
  81. if($service.HttpHeaders.ContainsKey("X-AnchorMailbox")){    
  82.     $service.HttpHeaders["X-AnchorMailbox"] = $MailboxName    
  83. }else{    
  84.     $service.HttpHeaders.Add("X-AnchorMailbox"$MailboxName);    
  85.     $service.HttpHeaders.Add("X-PreferServerAffinity","true");  
  86. }    
  87. "AnchorMailbox : " + $service.HttpHeaders["X-AnchorMailbox"]    
  88.   
  89.   
  90.   
  91. $FolderCollection = New-Object System.Collections.Hashtable  
  92. #Define Function to convert String to FolderPath    
  93. function ConvertToString($ipInputString){    
  94.     $Val1Text = ""    
  95.     for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  96.             $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  97.             $clInt++    
  98.     }    
  99.     return $Val1Text    
  100. }   
  101.   
  102. #Define Extended properties    
  103. $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  104. $folderidcnt = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)    
  105. #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  106. $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  107. #Deep Transval will ensure all folders in the search path are returned    
  108. $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;    
  109. $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  110. $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  111. #Add Properties to the  Property Set    
  112. $psPropertySet.Add($PR_Folder_Path);    
  113. $fvFolderView.PropertySet = $psPropertySet;    
  114. #The Search filter will exclude any Search Folders    
  115. $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  116. $fiResult = $null    
  117. #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox    
  118. do {    
  119.     $fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)    
  120.     foreach($ffFolder in $fiResult.Folders){    
  121.         $foldpathval = $null    
  122.         #Try to get the FolderPath Value and then covert it to a usable String     
  123.         if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  124.         {    
  125.             $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  126.             $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  127.             $hexString = $hexArr -join ''    
  128.             $hexString = $hexString.Replace("FEFF""5C00")    
  129.             $fpath = ConvertToString($hexString)    
  130.         }    
  131.         "FolderPath : " + $fpath    
  132.         $FolderCollection.Add($ffFolder.Id.UniqueId,$fpath)  
  133.     }   
  134.     $fvFolderView.Offset += $fiResult.Folders.Count  
  135. }while($fiResult.MoreAvailable -eq $true)    
  136.   
  137. function GetEventsRequest{    
  138. param (  
  139.   $SubscriptionId="$( throw 'SubscriptionId is a mandatory Parameter' )",  
  140.   $Watermark="$( throw 'Credentials is a mandatory Parameter' )",  
  141.   $ImpersonationHeader="$( throw 'ImpersonationHeader is a mandatory Parameter' )"  
  142. )  
  143. process{  
  144. Write-Host($SubscriptionId)  
  145. $request = @" 
  146. <?xml version="1.0" encoding="utf-8"?> 
  147. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
  148.   xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> 
  149.    <soap:Header> 
  150.     <t:RequestServerVersion Version="Exchange2010_SP2"/> 
  151.     <t:ExchangeImpersonation> 
  152.       <t:ConnectingSID> 
  153.         <t:SmtpAddress>$ImpersonationHeader</t:SmtpAddress> 
  154.       </t:ConnectingSID> 
  155.     </t:ExchangeImpersonation> 
  156.   </soap:Header> 
  157.   <soap:Body> 
  158.     <GetEvents xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"> 
  159.       <SubscriptionId>$SubscriptionId</SubscriptionId> 
  160.       <Watermark>$Watermark</Watermark> 
  161.     </GetEvents> 
  162.   </soap:Body> 
  163. </soap:Envelope> 
  164. "@  
  165. return $request  
  166. }  
  167. }  
  168. $DeletionsID = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsDeletions,$MailboxName);  
  169. $Deletions = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$DeletionsID)  
  170. $evEvents = new-object Microsoft.Exchange.WebServices.Data.EventType[] 6  
  171. $evEvents[0] = [Microsoft.Exchange.WebServices.Data.EventType]::Copied  
  172. $evEvents[1] = [Microsoft.Exchange.WebServices.Data.EventType]::Created  
  173. $evEvents[2] = [Microsoft.Exchange.WebServices.Data.EventType]::Deleted  
  174. $evEvents[3] = [Microsoft.Exchange.WebServices.Data.EventType]::Modified  
  175. $evEvents[4] = [Microsoft.Exchange.WebServices.Data.EventType]::Moved  
  176. $evEvents[5] = [Microsoft.Exchange.WebServices.Data.EventType]::NewMail  
  177. $psSub = ""  
  178. if(Test-Path -Path ("c:\temp\" + $MailboxName + "PullSubWM.wm")){ 
  179.     $psSub = Import-Csv -Path ("c:\temp\" + $MailboxName + "PullSubWM.wm") 
  180.     $request = GetEventsRequest -SubscriptionId $psSub.SubscriptionID -Watermark $psSub.Watermark -ImpersonationHeader $MailboxName 
  181.     $mbMailboxFolderURI = New-Object System.Uri($service.url)   
  182.     $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)   
  183.     $wrWebRequest.CookieContainer =  New-Object System.Net.CookieContainer    
  184.     $wrWebRequest.KeepAlive = $false;   
  185.     $wrWebRequest.Useragent = "EWS Script" 
  186.     $wrWebRequest.Headers.Set("Pragma", "no-cache");   
  187.     $wrWebRequest.Headers.Set("Translate", "f");   
  188.     $wrWebRequest.Headers.Set("Depth", "0");   
  189.     $wrWebRequest.ContentType = "text/xml";   
  190.     $wrWebRequest.ContentLength = $expRequest.Length;   
  191.     $wrWebRequest.Timeout = 60000;   
  192.     $wrWebRequest.Method = "POST";   
  193.     $wrWebRequest.Credentials = $creds   
  194.     $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($request);   
  195.     $wrWebRequest.ContentLength = $bqByteQuery.Length;   
  196.     $rsRequestStream = $wrWebRequest.GetRequestStream();   
  197.     $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);   
  198.     $rsRequestStream.Close();   
  199.     $wrWebResponse = $wrWebRequest.GetResponse();   
  200.     $rsResponseStream = $wrWebResponse.GetResponseStream()   
  201.     $sr = new-object System.IO.StreamReader($rsResponseStream);   
  202.     $rdResponseDocument = New-Object System.Xml.XmlDocument   
  203.     $rdResponseDocument.LoadXml($sr.ReadToEnd()); 
  204.     $rdResponseDocument.Envelope.Body.GetEventsResponse.ResponseMessages.GetEventsResponseMessage.ResponseClass 
  205.     if($rdResponseDocument.Envelope.Body.GetEventsResponse.ResponseMessages.GetEventsResponseMessage.ResponseClass -eq "Error"){ 
  206.          
  207.         $rdResponseDocument.Envelope.Body.GetEventsResponse.ResponseMessages.GetEventsResponseMessage | fl 
  208.         Remove-Item ("c:\temp\" + $MailboxName + "PullSubWM.wm") 
  209.         Write-Host ("Removed old watermarkFile") 
  210.     } 
  211.     else{ 
  212.         if($rdResponseDocument.Envelope.Body.GetEventsResponse.ResponseMessages.GetEventsResponseMessage.ResponseClass -eq "Success"){ 
  213.             $pullSubSav = "" | Select Watermark,SubscriptionID 
  214.             $wmark2 = $rdResponseDocument.getElementsByTagName("t:Watermark")            
  215.             $pullSubSav.Watermark = $wmark2.Item(($wmark2.Count-1))."#text"  
  216.             $pullSubSav.SubscriptionID = $rdResponseDocument.Envelope.Body.GetEventsResponse.ResponseMessages.GetEventsResponseMessage.Notification.SubscriptionId  
  217.             $pullSubSav | Export-Csv -Path ("c:\temp\" + $MailboxName + "PullSubWM.wm") -NoTypeInformation 
  218.             Write-Host "Subscription saved" 
  219.             $movedEvents = $rdResponseDocument.getElementsByTagName("t:MovedEvent")  
  220.             if($movedEvents.Count -gt 0){ 
  221.                 try{ 
  222.                     for($intmc=0;$intmc -lt $movedEvents.Count;$intmc++){ 
  223.                         $item = [Microsoft.Exchange.WebServices.Data.Item]::Bind($service, $movedEvents.Item($intmc).ItemId.Id); 
  224.                         $ItemEvt = "" | Select EventTime,MessageDateTimeReceived,Subject,MovedFrom,MovedTo,LastModifiedName 
  225.                         $ItemEvt.EventTime = $movedEvents.Item($intmc).TimeStamp 
  226.                         Write-Host ("Processing : " + $item.Subject) 
  227.                         write-host ("Last Modified by :" + $item.LastModifiedName) 
  228.                         $ItemEvt.Subject = $item.Subject  
  229.                         $ItemEvt.LastModifiedName = $item.LastModifiedName 
  230.                         $ItemEvt.MessageDateTimeReceived = $item.DateTimeReceived        
  231.                                  
  232.                         #$movedEvents.Item($intmc).OldItemId.Id 
  233.                      
  234.                         if($FolderCollection.containsKey($movedEvents.Item($intmc).OldParentFolderId.Id)){ 
  235.                             Write-Host ("Moved From Folder " + $FolderCollection[$movedEvents.Item($intmc).OldParentFolderId.Id]) 
  236.                             $ItemEvt.MovedFrom = $FolderCollection[$movedEvents.Item($intmc).OldParentFolderId.Id] 
  237.                         }                    
  238.                         if($FolderCollection.containsKey($movedEvents.Item($intmc).ParentFolderId.Id)){ 
  239.                             Write-Host ("Moved To Folder " + $FolderCollection[$movedEvents.Item($intmc).ParentFolderId.Id]) 
  240.                             $ItemEvt.MovedTo = $FolderCollection[$movedEvents.Item($intmc).ParentFolderId.Id] 
  241.                         } 
  242.                         else{ 
  243.                              if ($movedEvents.Item($intmc).ParentFolderId.Id -eq $Deletions.Id.UniqueId) 
  244.                              { 
  245.                                 Write-Host ("Moved to Recoverable Items - Deleted Items");                           
  246.                              } 
  247.                              $ItemEvt.MovedTo = "Moved to Recoverable Items - Deleted Items" 
  248.                         } 
  249.                         Add-Content -Path $Script:changeLog ($ItemEvt.EventTime + ",`"" + $ItemEvt.MessageDateTimeReceived + "`",`"" + $ItemEvt.Subject + "`"," + $ItemEvt.MovedFrom + "," + $ItemEvt.MovedTo + "," + $ItemEvt.LastModifiedName) 
  250.                     } 
  251.                 } 
  252.                 catch{ 
  253.                     Write-Host ($Error | fl) 
  254.                     $Error.Clear() 
  255.                 } 
  256.             } 
  257.  
  258.         } 
  259.     }    
  260. } 
  261. else{ 
  262.     $InboxId = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)   
  263.     $fldArray = new-object Microsoft.Exchange.WebServices.Data.FolderId[] 1   
  264.     $fldArray[0] = $InboxId   
  265.     $pullsub = $service.SubscribeToPullNotifications($fldArray,$duration, $WaterMark, $evEvents); 
  266.     $gEvents =  $pullsub.GetEvents(); 
  267.     $pullSubSav = "" | Select Watermark,SubscriptionID 
  268.     $pullSubSav.Watermark = $pullsub.Watermark 
  269.     $pullSubSav.SubscriptionID = $pullsub.Id 
  270.     $pullSubSav | Export-Csv -Path ("c:\temp\" + $MailboxName + "PullSubWM.wm") -NoTypeInformation 
  271.     Write-Host "Subscription saved"  
  272. }  



Viewing all 241 articles
Browse latest View live