Exchange Online offers three main possibilities to be accessed programmatically: Exchange Web Services API (EWS), a dedicated set of PowerShell cmdlets (called Exchange Online PowerShell), and, since the introduction of Microsoft Graph, the possibility to approach Exchange Online using REST APIs has been open. Because Graph is an ongoing project by Microsoft, the functionality it offers is not (yet) as complete as the possibilities given by EWS, but Graph is officially replacing EWS. The Exchange Online PowerShell is mainly used for configuration, monitoring, maintenance, and to manage Exchange from the command line.
EWS is becoming deprecated: Microsoft has announced (https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/) that starting on July 19th, 2018 "...Exchange Web Services (EWS) will no longer receive feature updates. While the service will continue to receive security updates and certain non-security updates, product design and features will remain unchanged. This change also applies to the EWS SDKs for Java and .NET as well."
|
EWS can be used in precisely the same way for Exchange Online and Exchange Onprem, the only difference is given by the disparities in functionality in the two systems (they are very similar in any way), and the login method. EWS can be used in different ways: As a managed API by any development language (making SOAP, Simple Object Access Protocol, calls under the hood), as a set of SOAP Web Services, and from PowerShell. Because SOAP has been replaced by REST in the enterprise and is, in fact, not used anymore, its programmatic use will be not discussed in this book.
Regarding PowerShell, there is a set of cmdlets dedicated to Exchange Online Protection (EOP), but they are only used in standalone EOP organizations (for example, to protect an OnPremises Exchange environment). If you are working with an Office 365 subscription that includes EOP (E3, E5, etc.), you don't use Exchange Online Protection PowerShell; the same features are available in the standard Exchange Online PowerShell modules.
|
As it happens with other servers from Office 365, it is necessary to log in to the system to be authenticated, before any program can start interacting with the data.
For the CSharp examples in this chapter, the values to log in to Exchange (email address, password, application ID, and tenant ID, depending on the authorization method) are saved in an external file called exCs.values.config that is used by the appSettings section in the App.Config Visual Studio Solution file. The App.Config file for the Visual Studio Solution contains the following section inside the <configuration> tag:
<appSettings file="c:\Temporary\exCs.values.config"> <add key="exUserName" value="" /> <add key="exUserPw" value="" /> <add key="exAppId" value="" /> <add key="exTenantId" value="" /> </appSettings>
|
The first line points to an external file that contains the values to be used by the App.Config file; it has the following form:
<appSettings > <add key="exUserName" value="user@domain.onmicrosoft.com" /> <add key="exUserPw" value="VerySecurePw" /> <add key="exAppId" value="SomeGuid" /> <add key="exTenantId" value="SomeGuid" /> </appSettings>
|
Note: The exAppId and exTenantId values are only needed if using oAuth (se section about oAuth later in this chapter).
The values can be called by their name in the appSettings file, as follows:
string myExUser = ConfigurationManager.AppSettings["exUserName"];
|
EWS can be used by PowerShell to reach programmatically the Exchange information. The Microsoft.Exchange.WebServices.dll needed to work with EWS must be installed locally.
The EWS DLLs can be downloaded from https://www.microsoft.com/en-us/download/details.aspx?id=42951. Download the EwsManagedApi.msi file from that site and install it locally. The DLLs will be installed in the local directory C:\Program Files\Microsoft\Exchange\Web Services\2.2\.
|
To make the EWS DLLs available for PowerShell, they must be loaded at the beginning of the script with the following statement (which must be in one continuous line):
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
|
The values to log in to Exchange (email address, password, application ID, and tenant ID, depending on the authorization method) are saved in an external file called exPs.values.config (in XML format) that is loaded by PowerShell at runtime. The external config file is called at the beginning of the script, as follows:
[xml]$configFile = get-content "C:\Projects\exPs.values.config"
|
And the file exPs.values.config contains the values to be used in the script; it has the following form:
<appSettings> <exUserName>user@domain.onmicrosoft.com</exUserName> <exUserPw>VerySecurePw</exUserPw> <exAppId>SomeGuid</exAppId> <exTenantId>SomeGuid</exTenantId> </appSettings>
|
Note: The exAppId and exTenantId values are only needed if using oAuth (se section about oAuth later in this chapter).
The values can be called by their name in the appSettings file, as follows:
$myUser = $configFile.appsettings.exUserPw
|
If PowerShell is not allowing to run scripts, change the execution policy using the command:
Set-ExecutionPolicy -ExecutionPolicy [Unrestricted]/[RemoteSigned]/[Default]
|
Microsoft recommends not to use basic authentication (using username/password) anymore, although Exchange Online still accepts this type of authentication. Nevertheless, basic authentication can be a good option, to avoid extensive setup tasks and repetitive logins, for simple test or demonstration applications.
EWS Basic Authentication is becoming fully decommissioned: Microsoft has announced (https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/) that on October 13th, 2020, it "...will stop supporting and fully decommission the Basic Authentication for EWS to access Exchange Online". This means that new or existing apps will not be able to use Basic Authentication when connecting to Exchange using EWS.
|
The function (ConnectBA) in the following recipe uses the email address and password of one user to get authorized in Exchange. The AutodiscoverUrl method determines the best endpoint for a given user (the endpoint that is closest to the user's Mailbox server); this method can be called using only the username parameter, but Exchange Online rejects the request as unsafe. Therefore, the RedirectionUrlValidationCallback routine, which is considered valid if it uses HTTPS, must be used in conjunction with the authentication call. Instantiating the ExchangeService with an empty constructor will create an instance that is bound to the latest known version of Exchange. The TraceEnabled and TraceFlag properties can be activated to get information from Exchange about the login process (for debugging purposes), and the Url method of the service instance gives back the address used by Exchange Online.
01.001 | ID | File | ||
Routines | ||||
NuGets | Microsoft.Exchange.WebServices, Microsoft.Identity.Client | |||
Ref. DLLs | Microsoft.Exchange.WebServices, Microsoft.Exchange.WebServices.Auth, Microsoft.Identity.Client | |||
Using | Microsoft.Exchange.WebServices.Data, Microsoft.Identity.Client |
static ExchangeService ConnectBA(string userEmail, string userPW) { ExchangeService exService = new ExchangeService { Credentials = new WebCredentials(userEmail, userPW) }; //exService.TraceEnabled = true; //exService.TraceFlags = TraceFlags.All; exService.AutodiscoverUrl(userEmail, RedirectionUrlValidationCallback); //Console.WriteLine(exService.Url); return exService; } static bool RedirectionUrlValidationCallback(string redirectionUrl) { bool validationResult = false; Uri redirectionUri = new Uri(redirectionUrl); if (redirectionUri.Scheme == "https") { validationResult = true; } return validationResult; }
The authorization method can be called from any other routine as follows:
01.002 | ID | File | ||
Routines | ||||
NuGets | Microsoft.Exchange.WebServices, Microsoft.Identity.Client | |||
Ref. DLLs | Microsoft.Exchange.WebServices, Microsoft.Exchange.WebServices.Auth, Microsoft.Identity.Client | |||
Using | Microsoft.Exchange.WebServices.Data, Microsoft.Identity.Client |
static void Main(string[] args) { ExchangeService myExService = ConnectBA( ConfigurationManager.AppSettings["exUserName"], ConfigurationManager.AppSettings["exUserPw"]); CallEWSTest(myExService); }
Although the fact that oAuth relies on a third-party authentication provider, that the standard is more difficult to implement than basic authentication, and that oAuth requires another layer of integration (the application will need both, the authentication provider and the Exchange server), Microsoft recommends using oAuth instead of basic authentication because of the advantage in security.
Since Office 365 uses Azure Active Directory (AAD) as authentication provider, any application that wants to use Office Exchange EWS oAuth authentication must have an application ID issued by AAD. The following steps indicate how to register one application as a public client with Azure Active Directory.
1 - Using a browser, navigate to the main administration page of Office 365 (https://admin.microsoft.com or through https://portal.office.com), log in with an administrator account, and open the Azure Active Directory Admin Center.
2 - Click on Azure Active Directory in the menu on the left side, and then on App registrations (Manage section). Use the New registration button.
3 - Assign a name to the registration, select Accounts in this organizational directory only in the Supported account types section, and select the Public client/native (mobile & desktop) option in the Redirect Uri section. Write the value urn:ietf:wg:oauth:2.0:oob on the textbox at the side of the Redirect Uri section. Use the Register button to save the registration.
4 - The registration is complete. Copy the values given in Application (client) ID and Directory (tenant) ID to use it in the source code of the application to be developed.
Note: If you are using the App.config file described in the initial section of this chapter, copy the Application (client) ID value to the exAppId parameter, and the Directory (tenant) ID value to the exTenantId parameter.
The Visual Studio solution to use the authentication from oAuth (a console applications in this chapter) requires a using directive to Microsoft.Identity.Client.
The DLLs to work with the Microsoft.Identity.Client can be installed by the NuGet Microsoft.Identity.Client by Microsoft (https://www.nuget.org/packages/Microsoft. Identity.Client/) directly from Visual Studio.
|
The function ConnectOA in the following recipe uses the Azure AD registration client and tenant ID to get authorized in Exchange.
01.003 | ID | File | ||
Routines | ||||
NuGets | Microsoft.Exchange.WebServices, Microsoft.Identity.Client | |||
Ref. DLLs | Microsoft.Exchange.WebServices, Microsoft.Exchange.WebServices.Auth, Microsoft.Identity.Client | |||
Using | Microsoft.Exchange.WebServices.Data, Microsoft.Identity.Client |
static async System.Threading.Tasks.Task<ExchangeService> ConnectOA( string AppId, string TenId) { ExchangeService exService = new ExchangeService(); PublicClientApplicationOptions pcaOptions = new PublicClientApplicationOptions { ClientId = AppId, TenantId = TenId }; IPublicClientApplication pcaBuilder = PublicClientApplicationBuilder .CreateWithApplicationOptions(pcaOptions).Build(); string[] exScope = new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" }; AuthenticationResult authToken = await pcaBuilder.AcquireTokenInteractive(exScope).ExecuteAsync(); exService.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"); exService.Credentials = new OAuthCredentials(authToken.AccessToken); return await System.Threading.Tasks.Task.FromResult(exService); }
The first time that the application runs, a standard login window will appear requiring the account data of the user that made the registration. After login, the window will ask for permissions (Access your mailboxes, Maintain access to data you have given it access to and View your basic profile). Subsequently, the application will ask only for the user login, not for the permissions.
Because the connection routine is asynchronous, use the following code to call it.
01.004 | ID | File | ||
Routines | ||||
NuGets | Microsoft.Exchange.WebServices, Microsoft.Identity.Client | |||
Ref. DLLs | Microsoft.Exchange.WebServices, Microsoft.Exchange.WebServices.Auth, Microsoft.Identity.Client | |||
Using | Microsoft.Exchange.WebServices.Data, Microsoft.Identity.Client |
static void Main(string[] args) { ExchangeService myExService = ConnectOA( ConfigurationManager.AppSettings["exAppId"], ConfigurationManager.AppSettings["exTenantId"]). GetAwaiter().GetResult(); CallEWSTest(myExService); }
The use of oAuth to get access to Exchange is faster than the use of base authentication, especially because it is not necessary to use the auto-discovery method.
|
The ConnectPsEwsBA routine in the next recipe takes care of login in Exchange using Basic Authentication with PowerShell.
01.005 | ID | File | ||
Routines | ||||
PS Modules | GenericOauthEWS.ps1 | |||
Other Modules | Microsoft.Exchange.WebServices.dll |
Function ConnectPsEwsBA() { $ExService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService $ExService.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials(` $configFile.appsettings.exUserName, $configFile.appsettings.exUserPw) $ExService.Url = new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx"); #$ExService.TraceEnabled = $true #$ExService.TraceFlags = [Microsoft.Exchange.WebServices.Data.TraceFlags]::All $ExService.AutodiscoverUrl($configFile.appsettings.exUserName, {$true}) return $ExService }
See section 1.2.2. to get details about login with PowerShell. The TraceEnabled and TraceFlag properties can be activated at any moment to get information regarding the internal working of Exchange when logging in.
To call the function, use code similar to the next example.
01.006 | ID | File | ||
Routines | ||||
PS Modules | GenericOauthEWS.ps1 | |||
Other Modules | Microsoft.Exchange.WebServices.dll |
##==> EWS Basic Authorization Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" $ExService = ConnectPsEwsBA CallEWSTest $ExService #Calling any function
Using oAuth from PowerShell is not a trivial or easy endeavor. But because Basic Authentication is being closed for Exchange, it will be obligatory to use in some years.
For this book, we use the GenericOauthEWS.ps1 login routine developed by Glen Scales (glenscales@yahoo.com, https://gsexdev.blogspot.com/), that can be downloaded from his GitHub repository https://github.com/gscales/Powershell-Scripts/blob/master/GenericOauthEWS.ps1. The module is fully described in the article https://gsexdev.blogspot.com/2018/08/dependency-free-generic-ews-oauth.html.
The EWS DLLs required to work with oAuth and Exchange can be downloaded from https://www.microsoft.com/en-us/download/details.aspx?id=42951. Download the EwsManagedApi.msi file from that site and install it locally. The DLLs will be installed in the local directory C:\Program Files\Microsoft\Exchange\Web Services\2.2\.
|
To use the module, first, load it in the script and then call the Connect-Exchange method. Use the return value to get any access to the required information.
01.007 | ID | File | ||
Routines | ||||
PS Modules | GenericOauthEWS.ps1 | |||
Other Modules | Microsoft.Exchange.WebServices.dll |
##==> EWS oAuth Authorization Import-Module .\GenericOauthEWS.ps1 -Force #Test-EWSConnection -MailboxName $configFile.appsettings.exUserName $ExService = Connect-Exchange ` $configFile.appsettings.exUserName "" $configFile.appsettings.exAppId CallEWSTest $ExService #Calling any function
The module has a Test-EWSConnection method that can be used to check the connection with Exchange and get some information about the account.
To work with Exchange Online PowerShell, use Windows PowerShell on the local computer to create a remote PowerShell session with Exchange Online, providing the Office 365 credentials, the required connection settings, and then import the Exchange Online cmdlets into the local Windows PowerShell session.
The following function automates the complete process.
01.008 | ID | File | ||
Routines | ||||
PS Modules | GenericOauthEWS.ps1 | |||
Other Modules | Microsoft.Exchange.WebServices.dll |
Function ConnectPsOnlBA() { [SecureString]$securePW = ConvertTo-SecureString -String ` $configFile.appsettings.exUserPw -AsPlainText -Force $myCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList ` $configFile.appsettings.exUserName, $securePW $mySession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ` https://outlook.office365.com/powershell-liveid/ -Authentication Basic ` -AllowRedirection -Credential $myCredentials Import-PSSession $mySession -AllowClobber }
To use the remote session, utilize the function as follows.
01.009 | ID | File | ||
Routines | ||||
PS Modules | GenericOauthEWS.ps1 | |||
Other Modules | Microsoft.Exchange.WebServices.dll |
##==> Exchange Online PowerShell Basic Authorization ConnectPsOnlBA Get-Mailbox #Calling any cmdlet $currentSession = Get-PSSession Remove-PSSession -Session $currentSession
The remote session is killed at the end of the code for security concerns.
Microsoft has announced that Remote PowerShell for Exchange will be closed on October 13th, 2020 (https://techcommunity.microsoft.com/t5/blogs/ blogarticleprintpage/blog-id/Exchange/article-id/27095). Microsoft recommends using the multi-factor authentication PowerShell module or the PowerShell within Azure Cloud Shell to use PowerShell with Exchange, as the article mentions.
|
To work with EWS in Visual Studio, the development computer must have the Microsoft.Exchange.WebServices and Microsoft.Exchange.WebServices.Auth DLLs installed.
The EWS DLLs can be installed by the NuGet Microsoft.Exchange.WebServices by Microsoft (https://www.nuget.org/packages/Microsoft. Exchange.WebServices/) directly from Visual Studio.
|