Searching contacts using EWS Managed API 1.1 from PowerShell (Impersonation, SearchFilterCollection, ContainsSubstring)

Here is another example of using EWS Managed API with PowerShell 2.0. In this case the requirement was to be able to find out all contacts that have Hotmail email address in any of the Email Address fields. This provided me to opportunity to demonstrate the use SearchFilterCollection and ContainsSubstring search filter from a PowerShell script. In addition to SearchFilterCollection and ContainsSubstring, this sample also shows the use of Impersonation. The use of Impersonation is necessary because the requirement was to search in multiple mailboxes.

Details on how to configure Exchange Impersonation can be found in the links below:
Exchange 2010:
Configuring Exchange Impersonation (Exchange Web Services)

Using Exchange Impersonation XML Request/Response

Using Impersonation in EWS Managed API

Exchange 2007:
Configuring Exchange Impersonation (Exchange Web Services)

Using Exchange Impersonation (Exchange Web Services)

You will also need to download and install the EWS Managed API on the box you run this script on from the link below:

Exchange Web Services Managed API

Now for the code. Create a .PS1 file with the code below:

 # This script requires PowerShell 2.0
# Make sure the Import-Module command below matches the DLL location of the API.
# This path must match the install location of the EWS managed API. Change it if needed.[string]$info = "White"                # Color for informational messages
[string]$warning = "Yellow"            # Color for warning messages
[string]$error = "Red"                # Color for error messages
[string]$LogFile = "C:\Temp\Log.txt"        # Path of the Log Filefunction SearchContacts($MailboxName)
    Write-host "Searching Contacts in Mailbox :" $MailboxName -foregroundcolor  $info
    Add-Content $LogFile ("Searching Contacts in Mailbox :" + $MailboxName)    #Change the user to Impersonate
    $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId(
                            [Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);    #Number of Items to Get
    $pageSize =40
    $Offset = 0
    $MoreItems =$true

    while ($MoreItems)
         #Setup the View to get a limited number of Items at one time
        $itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView($pageSize,$Offset,[Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
        $itemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow

        $itemView.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.ContactSchema]::GivenName,
                    [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress3)        #Create the Search Filter.
        $searchFilterEA1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(

        $searchFilterEA2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(

        $searchFilterEA3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring(

        $oSearchFilters = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection(

        $oSearchFilters.add($searchFilterEA3)        $oFindItems = $service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts,$oSearchFilters,$itemView)
        foreach ($Item in $oFindItems.Items)

           if ($Item.GetType().FullName -eq "Microsoft.Exchange.WebServices.Data.Contact")
                  Write-host "Given Name:" $Item.GivenName
                  Write-host "Surname:" $Item.Surname
                  Write-host "Email Address 1:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1]
                  Write-host "Email Address 2:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress2]
                  Write-host "Email Address 3:" $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress3]    

                  Add-Content $LogFile("Given Name :" + $Item.GivenName)
                  Add-Content $LogFile("Surname :" + $Item.Surname)
                  Add-Content $LogFile("Email Address 1:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1])
                  Add-Content $LogFile("Email Address 2:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress2])
                  Add-Content $LogFile("Email Address 3:" + $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress3])   


     if ($oFindItems.MoreAvailable -eq $false)
        {$MoreItems = $false}

         if ($MoreItems)
        {$Offset += $pageSize}

    Write-host "Searching Contacts in Mailbox :" $MailboxName "Done!" -foregroundcolor  $info
    Add-Content $LogFile ("Done!")
    Add-Content $LogFile ("-------------------------------------------------" )    

    $service.ImpersonatedUserId = $null

Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll"# This script also works on Exchange 2007. Just change the Version Header to Exchange2007_SP1
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)# Set the Credentials
$service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials("serviceAccount","Password","domain")# Change the URL to point to your cas server
$service.Url= new-object Uri(“http://Your-CAS-Server/EWS/Exchange.asmx”)# Set $UseAutoDiscover to $true if you want to use AutoDiscover else it will use the URL set above
$UseAutoDiscover = $false

#Read data from the UserAccounts.txt
import-csv UserAccounts.txt | foreach-object {
    $WindowsEmailAddress = $_.WindowsEmailAddress.ToString()

    if ($UseAutoDiscover -eq $true) {
        Write-host "Autodiscovering.." -foregroundcolor $info
        $UseAutoDiscover = $false
        Write-host "Autodiscovering Done!" -foregroundcolor $info
        Write-host "EWS URL set to :" $service.Url -foregroundcolor $info

    #To catch the Exceptions generated
    trap [System.Exception] 
        Write-host ("Error: " + $_.Exception.Message) -foregroundcolor $error;
        Add-Content $LogFile ("Error: " + $_.Exception.Message);

UserAccounts.txt contains the list of the users mailboxes that need to be searched. This file should exist in the same directory as the .PS1 file.The First row denotes the Field Names. Format of the text file is as below:


To Run the script:

1)Open the Exchange Management Shell

2)Navigate to the location where the script file is.

3)Type in .\ScriptName.PS1 and hit enter to execute the script

Note: This script creates a log file in C:\Temp. Make sure that directory exists.