none
Exchange Web Services & Powershell - Contact Update Issues RRS feed

  • Question

  • Hi All,

    I am using EWS with Powershell for the first time, and although my code works, I have run across an issue I am not able to fix. I am trying to fix users' contacts that have been imported incorrectly from another mail system, which is working fine for most entries. However, when an email address field includes a colon (:) when I call the address using the following:

    ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address)

    it does not read the address. I have identified this doesn't appear to be a code issue, as when I replace the address with another, still with invalid characters, such as < and spaces, this works fine. I presume this would be a conflict with the EWS syntax and use of : within the syntax itself.

    I have tried finding a way to separate the "EmailAddress1" part so that the colon included within the address field doesn't affect the script but haven't found a successful way of doing this. I hope all the above makes sense. Is there anyone who is able to direct me to a solution so we can call the email addresses that contain the invalid colons? 

    Many thanks,

    Mike Parker


    Mike Parker, MCSE - Messaging, www.cloudbusiness.com 


    Tuesday, February 3, 2015 1:27 PM

Answers

  • Here's my entire script:

    $dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
    [void][Reflection.Assembly]::LoadFile($dllpath)
    
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)
    $uri = [system.URI] "https://outlook.office365.com/ews/exchange.asmx"
    $service.Url = $uri
    
    $service.Credentials = New-Object System.Net.NetworkCredential("user@contoso.com", "password")
    
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts)
    
    $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(100)      
    $psPropset = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)   
    
    $Contacts = $ContactsFolder.FindItems($ivItemView)
    
    [void]$service.LoadPropertiesForItems($Contacts, $psPropset)
    
    ForEach($Contact in $Contacts.Items) {
      Write-Host ($Contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] | Format-List | Out-String)
    }

    I used LoadPropertiesForItems since it does it all in one shot. It should be the same as Contact.Load, so I wouldn't worry about that.

    • Marked as answer by MikeParker104 Thursday, February 5, 2015 8:59 AM
    Wednesday, February 4, 2015 3:26 PM
    Moderator

All replies

  • Hi All,

    I am using EWS with Powershell for the first time, and although my code works, I have run across an issue I am not able to fix. I am trying to fix users' contacts that have been imported incorrectly from another mail system, which is working fine for most entries. However, when an email address field includes a colon (:) when I call the address using the following:

    ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address)

    it does not read the address. I have identified this doesn't appear to be a code issue, as when I replace the address on the same contact with another, still with invalid characters, such as < and spaces, this works fine. I presume this would be a conflict with the EWS syntax and use of : within the syntax itself.

    So just to clarify, if my contact has an email address value of "mailto:firstnamelastname@domain.com" it will not be retrieved, but if it is something like "FirstName LastName <firstnamelastname@domain.com>" it will get retrieved.

    I have tried finding a way to separate the "EmailAddress1" part so that the colon included within the address field doesn't affect the script but haven't found a successful way of doing this. I hope all the above makes sense. Is there anyone who is able to direct me to a solution so we can call the email addresses that contain the invalid colons? 

    Many thanks,

    Mike Parker


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com




    • Edited by MikeParker104 Tuesday, February 3, 2015 1:41 PM
    • Merged by AnnaWY Thursday, March 5, 2015 6:09 AM duplicate
    Tuesday, February 3, 2015 1:38 PM
  • You can test for the colon and if it exists then split the address - 

    if ($contact.EmailAddress -like "*:*") {
    
    $contact.EmailAddress = (($contact.EmailAddress).Split(":"))[1]
    
    }

    Tuesday, February 3, 2015 2:18 PM
  • Hi,

    Thanks for the answer. I don't know whether I am misunderstanding the issue, but I am not able to Access the $contact.EmailAddress to view the addresses, and therefore, check for the colon.

    If I do $contact.EmailAddress I get the following: Microsoft.Exchange.WebServices.Data.EmailAddressDictionary

    Which is why I run

    ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address)

    in the example. Am I missing something here? Is there a simpler way to access the list of email addresses? In my research I haven't seen anything but I may have missed something.

    Thanks,
    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Tuesday, February 3, 2015 2:22 PM
  • Ah I see, I've never used EWS so i may not be much help. Are you able to retrieve an address by specifying an index? $contact.EmailAddresses[0]?
    Tuesday, February 3, 2015 2:33 PM
  • Yes, I see this too. The server doesn't return the address because it's invalid. However, I noticed that it does return a Name for the EmailAddress entry, which contains the invalid address, so you might be able to do some string manipulation to extract the value.

    In other words, this:

    ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Name)

    Gives this:

    "Colon Man (colon:foo@bar.com)"

    Tuesday, February 3, 2015 2:42 PM
    Moderator
  • Hi Jason,

    Thanks for the response. This would possibly be an option, however, on my test users I am not getting the Name field either. I just created a new contact to test this. In OWA the contact looks like this: 

    

    But if I run the following in Powershell:

    foreach($contact in $contacts){($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1])}

    I get 5 of the following (I have added colons to each of the contacts for testing purposes):

    Name        :
    Address     :
    RoutingType :
    MailboxType :
    Id          :

    Any more ideas? =)

    Thanks,

    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Tuesday, February 3, 2015 2:53 PM
  • It would help to see more of your code. Did you call Contact.Bind to load all the properties?
    Tuesday, February 3, 2015 2:56 PM
    Moderator
  • Please see the relevant portion of code below. If you need any more please let me know:

    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts)
    $Contacts = $ContactsFolder.FindItems(100)
    ForEach($contact in $contacts) {
    
    	$error.clear()
    	$iter1 = ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address)
    	$iter2 = ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress2].Address)
    	$iter3 = ($contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress3].Address)
    	$enumEmailAddress1 = [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress1;
    	$enumEmailAddress2 = [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress2;
    	$enumEmailAddress3 = [Microsoft.Exchange.WebServices.Data.ContactSchema]::EmailAddress3;
    	$enumDisplayName = [Microsoft.Exchange.WebServices.Data.ContactSchema]::DisplayName
    
    
    	$DisplayName = $contact.DisplayName
    	# write-host "$DisplayName"
    	$iter = 1
    	$LogOutput = $DisplayName 
    	while ($iter -lt 4){
    		$error.clear()
    		Write-Host "Working on Email Address $iter of contact $DisplayName";
    		if ($iter -eq 1){
    			$contactEmail = $iter1
    			$enumEmailAddress = $enumEmailAddress1
    		}
    		elseif($iter -eq 2){
    			$contactEmail = $iter2
    			$enumEmailAddress = $enumEmailAddress2
    		}
    		elseif($iter -eq 3){
    			$contactEmail = $iter3
    			$enumEmailAddress = $enumEmailAddress3
    		}
    
    		$LogOutput = $LogOutput + "," + $contactEmail
    		if(($contactEmail -eq "mailto:toi.smdioa@beicn.com") -or ($contactEmail -like "*<*") -or ($contactEmail -like "*>*")){
    			$contactEmail = $contactEmail.Split('<')[1]
    			$contactEmail = $contactEmail.Split('>')[0]
    			$contactEmail = $contactEmail -replace ("mailto:","")
    			$contactEmail = $contactEmail -replace ("'","")
    
    			$Contact.EmailAddresses[$enumEmailAddress] = $contactEmail
    			$LogOutput = $LogOutput + "," + $contactEmail
    
    		}
    		$iter++
    	} # End of While
    	$Contact.Update($enumAlwaysOverWrite)
    
    	#.ADD Output the $LogOutput to the log file here
    	Out-File -FilePath $logCSV -InputObject $LogOutput -Encoding UTF8 -append
    
    } # End of ForEach Contact

    Thanks again for your help! 

    Mike 



    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Tuesday, February 3, 2015 3:05 PM
  • Try calling Contact.Load before accessing the email properties to ensure that all of the relevant properties are loaded.

    Tuesday, February 3, 2015 3:26 PM
    Moderator
  • Sorry to be a pain, would you be able to provide an example of how this would be used in this situation?

    Not familiar with the Contact.Load() 

    Thanks,

    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Tuesday, February 3, 2015 3:32 PM
  • From the MSDN documentation , I see that the class EmailAddressDictionary has a tryGetValue method on it.

    https://msdn.microsoft.com/en-us/library/ee356483%28v=exchg.80%29.aspx

    You can try invoking it, if the suggestion from Braham doesn't work out.
    For Example something like below :

    $contact.EmailAddresses.tryGetValue([Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1)
    
    EmailAddress1 is the enum for the first email address.
    Look here for the enumeration details : https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.emailaddresskey%28v=exchg.80%29.aspx
    Hope this helps

    Knowledge is Power{Shell}

    DexterPOSH

    My Blog

    Tuesday, February 3, 2015 4:56 PM
  • Ok, so I actually tried this from Powershell, and I reproduced it when creating the ExchangeService object while specifying Exchange2007_SP1. When I changed it to Exchange2013_SP1 it worked. What version of Exchange are you on?
    Tuesday, February 3, 2015 5:33 PM
    Moderator
  • Hi Jason,

    Thanks for coming back again. I believe I am setting the Exchange version to 2010, so I will set it to 2013 when I have the code to hand tomorrow and update you.

    Thanks again,

    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Tuesday, February 3, 2015 8:02 PM
  • Hi Jason,

    I have just run again and checked the Exchange version. I am connecting to Office 365 to test, but in the live version I will be connecting to Exchange 2013 SP1. I have re-tested this morning, ensuring the Exchange version in my service is correct as below:

    Url                          : https://outlook.office365.com/EWS/Exchange.asmx
    ImpersonatedUserId           : Microsoft.Exchange.WebServices.Data.ImpersonatedUserId
    ManagementRoles              :
    PreferredCulture             :
    DateTimePrecision            : Default
    FileAttachmentContentHandler :
    TimeZone                     : (UTC) Dublin, Edinburgh, Lisbon, London
    UnifiedMessaging             : Microsoft.Exchange.WebServices.Data.UnifiedMessaging
    EnableScpLookup              : True
    TraceEnablePrettyPrinting    : True
    CookieContainer              : System.Net.CookieContainer
    SendClientLatencies          : True
    TraceEnabled                 : False
    TraceFlags                   : All
    TraceListener                : Microsoft.Exchange.WebServices.Data.EwsTraceListener
    Credentials                  : Microsoft.Exchange.WebServices.Data.WebCredentials
    UseDefaultCredentials        : False
    Timeout                      : 100000
    PreAuthenticate              : False
    AcceptGzipEncoding           : True
    RequestedServerVersion       : Exchange2013_SP1
    UserAgent                    : ExchangeServicesClient/15.00.0847.030
    ServerInfo                   : 15.01.0065.017
    WebProxy                     :
    KeepAlive                    : True
    ConnectionGroupName          :
    ClientRequestId              :
    ReturnClientRequestId        : False
    HttpHeaders                  : {}
    HttpResponseHeaders          : {[Transfer-Encoding, chunked], [Content-Encoding, gzip], [Vary, Accept-Encoding], [request-id,
                                   4a70bed1-e965-4ac6-9cb7-b210b2a646ff,4a70bed1-e965-4ac6-9cb7-b210b2a646ff]...}
    


    Still get the same response, I have 3 contacts, 2 in correct format and 1 containing the colon. When I run:

    Foreach($contact in $contacts) {$contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1].Address;}

    it will only return the 2 contacts without a colon! :-@

    Jason, when you get the correct results, is this when using a Contact.Load because I still haven't been able to work that one out...

    Thanks,
    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Wednesday, February 4, 2015 9:53 AM
  • Here's my entire script:

    $dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
    [void][Reflection.Assembly]::LoadFile($dllpath)
    
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)
    $uri = [system.URI] "https://outlook.office365.com/ews/exchange.asmx"
    $service.Url = $uri
    
    $service.Credentials = New-Object System.Net.NetworkCredential("user@contoso.com", "password")
    
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts)
    
    $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(100)      
    $psPropset = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)   
    
    $Contacts = $ContactsFolder.FindItems($ivItemView)
    
    [void]$service.LoadPropertiesForItems($Contacts, $psPropset)
    
    ForEach($Contact in $Contacts.Items) {
      Write-Host ($Contact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] | Format-List | Out-String)
    }

    I used LoadPropertiesForItems since it does it all in one shot. It should be the same as Contact.Load, so I wouldn't worry about that.

    • Marked as answer by MikeParker104 Thursday, February 5, 2015 8:59 AM
    Wednesday, February 4, 2015 3:26 PM
    Moderator
  • Hi Jason,

    I tested this with EWS API version 2.1 and it was not working, upgraded to 2.2 as yours and this now loads the display name but still not the email address. This is workable though because at least we will have a log of the display name for items that are now blank.

    Thanks for all your help, if I make any further progress I will update the thread! 

    Thanks,

    Mike


    Mike Parker | MCSE - Messaging | www.cloudbusiness.com</ a>

    Thursday, February 5, 2015 8:59 AM