Link to home
Start Free TrialLog in
Avatar of Scott Townsend
Scott TownsendFlag for United States of America

asked on

EWS Impersonation in Hybrid Environment - Cant find user in Office365

I have some Code on our Intranet that Syncs Employee contacts with the Users Outlook Contacts.

The code has been working great for On-Premise users for years. I am testing the Migration of users to O365. When The user used the code below while still residing in the On-Premise server it works great.  After the Migration the code fails with 'Object reference not set to an instance of an object.' Though I'm still working with the WebTeam on what line the Error is coming from.

When using EWS what is the Flow for how it knows where the mailbox resides?  It would be great if there was Minimal Change to the Code.

I have converted other code that accesses the Office 365 Server Directly through EWS, though I would assume it would have an issue finding an On-Premise Mailbox.

Thanks,
  Scott<-

        Private Const ExchangeURL As String = "https://On-PremiseServer/EWS/Exchange.asmx"
        Private Const ExchangeUserName As String = "UserWithImpersonationRights"
        Private Const ExchangePassword As String = "UserPassword"
        Private Const ExchangeDomain As String = "AD-Domain"



        Private Function GetEWSConnection(ByVal UserEmailAddress As String) As ExchangeService
            Static EWS As ExchangeService = Nothing
            Static LastUserEmailAddress As String = String.Empty
            If UserEmailAddress <> LastUserEmailAddress Then EWS = Nothing
            If EWS Is Nothing Then
                EWS = New ExchangeService(ExchangeVersion.Exchange2010_SP1)

                '**********************************************************************************************
                'Dim listener As New ExchangeTraceListener
                'EWS.TraceListener = listener
                'EWS.TraceFlags = TraceFlags.EwsRequest Or TraceFlags.EwsResponse Or TraceFlags.DebugMessage
                'EWS.TraceEnabled = True
                '**********************************************************************************************

                EWS.Credentials = New WebCredentials(ExchangeUserName, ExchangePassword, ExchangeDomain)
                EWS.Url = New Uri(ExchangeURL)
                ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidationCallback
                EWS.ImpersonatedUserId = New ImpersonatedUserId(ConnectingIdType.SmtpAddress, UserEmailAddress)
            End If
            Return EWS
        End Function



        Private Shared Function CertificateValidationCallback(sender As Object, certificate As X509Certificate, chain As X509Chain, sslPolicyErrors As SslPolicyErrors) As Boolean
            If sslPolicyErrors = Security.SslPolicyErrors.None Then
                Return True
            End If
            Return False
        End Function

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Sunil Chauhan
Sunil Chauhan
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
make sure when its identified that the user is O365, then your o365 admin account with application impersonation rights is passed to EWS service for authentication.
Avatar of Scott Townsend

ASKER

I think I was impatient. After doing both of the above, I tried my code and it was saying I still didn't have Impersonation rights.   Asked a co-worker to look at my code 10 minutes later to show him the issue and where it was failing and it didn't fail. So it must of taken some time to replication the permissions to whichever server that EWS was connecting to.

Thank you!
After using AutoDiscovery and setting the impersonation permissions I was good to go...
For anyone interested here is my Sample code to read the subject of the first item in a mailbox that is either hosted or on-premise.

Option Strict On
Option Explicit On


Imports System.Net
Imports System.Net.Security
Imports System.Security.Cryptography.X509Certificates
Imports Microsoft.Exchange.WebServices.Data
Imports Microsoft.Exchange.WebServices.Autodiscover
Imports System.Xml


Public Class Form1
    Private EWS As ExchangeService = Nothing

    Private Const strO365ADURL As String = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml"
    Private Const strOPADURL As String = "https://<yourOn-PremMailServer>.com/autodiscover/autodiscover.xml"

    Private Sub SetupEWS()
        'Clear out the URL and Status
        tbEWSURL.Text = ""
        tbConnectionStatus.Text = ""

        ' Set the Version of Exchange to the ON-PRemise Server version
        EWS = New ExchangeService(ExchangeVersion.Exchange2010_SP2)
        'Set our User Credentials
        EWS.Credentials = New WebCredentials(tbEWSUserName.Text, tbEWSPassword.Text)
        'Use AutoDiscover to find the Connection URL for the mailbox we are looking at accessing. 
        tbConnectionStatus.Text = "Attempting Audiscover for user:" + tbEWSImpersonationUser.Text.ToString
        tbEWSURL.Text = ""
        Cursor = Cursors.WaitCursor
        Try
            EWS.AutodiscoverUrl(tbEWSImpersonationUser.Text, AddressOf AutoDiscoverURLValidationCallback)
            Cursor = Cursors.Default
            tbEWSURL.Text = EWS.Url.ToString
            'If there is some funniness with the SSL on the Connection Set our CertificateValidationCallback, We dont care about Warnings vs Errors.
            ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidationCallback
            'Let the EWS Connection know we want to access someone elses mailbox
            EWS.ImpersonatedUserId = New ImpersonatedUserId(ConnectingIdType.SmtpAddress, tbEWSImpersonationUser.Text)

        Catch ex As Exception When TypeOf ex Is AutodiscoverRemoteException OrElse TypeOf ex Is AutodiscoverLocalException
            Dim strErrorMessage As String = ""
            If ex.GetType = GetType(AutodiscoverRemoteException) Then
                'Handle exception type AutodiscoverRemoteException
                strErrorMessage = DirectCast(ex, AutodiscoverRemoteException).Error.Message
            ElseIf ex.GetType = GetType(AutodiscoverLocalException) Then
                'Handle exception type AutodiscoverLocalException
                strErrorMessage = DirectCast(ex, AutodiscoverLocalException).Message + " Could Not Connect to AutoDiscover URL!"
            End If


            'MsgBox("Exception thrown: " + strErrorMessage, MsgBoxStyle.Exclamation, "Autodiscover service Error")
            tbConnectionStatus.Text = strErrorMessage
            tbEWSURL.Text = ""
            Cursor = Cursors.Default
        End Try

    End Sub
    ' See if there was just a SSL Warning vs an Error. If only a Warning (Self Signed)  and no Errors then return True
    Private Shared Function CertificateValidationCallback(sender As Object, certificate As X509Certificate, chain As X509Chain, sslPolicyErrors As SslPolicyErrors) As Boolean
        Dim bSSLNoError As Boolean = False
        If sslPolicyErrors = Security.SslPolicyErrors.None Then
            bSSLNoError = True
        End If
        Return bSSLNoError
    End Function

    ' Be sure we are talking to whom we think we should be asking for the EWS URL
    ' We should get an O365 URL or a On-Premise URL
    Private Shared Function AutoDiscoverURLValidationCallback(url As String) As Boolean
        Dim bURLOk As Boolean = False
        If url.ToLower().StartsWith(strO365ADURL) Or url.ToLower().StartsWith(strOPADURL) Then
            bURLOk = True
        End If
        Return bURLOk
    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        SetupEWS()

    End Sub

    Private Sub btnGetEmail_Click(sender As Object, e As EventArgs) Handles btnGetEmail.Click

        Dim inboxItems As FindItemsResults(Of Item)
        ' Create a view with a page size of 1.
        Dim view As New ItemView(1)
        view.PageSize = 1
        'Identify the Subject and DateTimeReceived properties to return.
        'Indicate that the base property will be the item identifier
        view.PropertySet = (New PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject))

        ' Send the request to search the Inbox and get the results.
        inboxItems = EWS.FindItems(WellKnownFolderName.Inbox, view)

        ' Process each item.
        For Each item As Item In inboxItems.Items
            If TypeOf item Is EmailMessage Then
                tbEmailSubject.Text = TryCast(item, EmailMessage).Subject.ToString
            Else
                ' Else handle other item types.
            End If
        Next
    End Sub
End Class

Open in new window