Testing Exchange Autodiscover with PowerShell and the EWS Managed API

by Mike Pfeiffer on August 24, 2011

Have you ever used that "Test E-mail AutoConfiguration" utility in Outlook? It's a useful tool that will query the autodiscover service, showing you exactly what CAS URLs are being handed to a particular user, and what the MAPI/HTTP endpoint is for their Exchange mailbox. You can run this tool by pressing the Ctrl button, right clicking on the Outlook 2007/2010 tray icon, and selecting "Test E-mail AutoConfiguration". This is nice, but there are times when I want to be able to determine this information for a user without having to fire up Outlook, or when Outlook isn't installed where ever I happen to be at the moment. My solution for this is a PowerShell function that will retrieve this information using the EWS Managed API. Here's the code:

function Test-Autodiscover {
    [CmdletBinding()]
    param(
      [Parameter(Position=1, Mandatory=$true)]
      [String]
      $EmailAddress,
      
      [ValidateSet("Internal", "External")]
      [Parameter(Position=2, Mandatory=$false)]
      [String]
      $Location = "External",      
      
      [Parameter(Position=3, Mandatory=$false)]
      [System.Management.Automation.PSCredential]
      $Credential,
      
      [Parameter(Position=4, Mandatory=$false)]
      [switch]
      $TraceEnabled,
      
      [Parameter(Position=5, Mandatory=$false)]
      [bool]
      $IgnoreSsl = $true,
      
      [Parameter(Position=6, Mandatory=$false)]
      [String]
      $Url
      )
      
      begin{
        Add-Type -Path 'C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll'
      }
      
      process {
        $autod = New-Object Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverService
        $autod.RedirectionUrlValidationCallback = {$true}
        $autod.TraceEnabled = $TraceEnabled
        
        if($IgnoreSsl) {
          [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}        
        }
        
        if($Credential){
          $autod.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $Credential.UserName, $Credential.GetNetworkCredential().Password
        }
        
        if($Url) {
          $autod.Url = $Url
        }
        
        switch($Location) {
          "Internal" {
            $autod.EnableScpLookup = $true
            $response = $autod.GetUserSettings(
              $EmailAddress,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEcpUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEwsUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalOABUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalUMUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalWebClientUrls
            )
            
            New-Object PSObject -Property @{
              RpcClientServer = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalRpcClientServer]
              InternalOwaUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalWebClientUrls].urls[0].url
              InternalEcpUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEcpUrl]
              InternalEwsUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalEwsUrl]
              InternalOABUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalOABUrl]
              InternalUMUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::InternalUMUrl]
            }
          }
          "External" {
            $autod.EnableScpLookup = $false
            $response = $autod.GetUserSettings(
              $EmailAddress,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalMailboxServer,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEcpUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalOABUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalUMUrl,
              [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalWebClientUrls
            )
            
            New-Object PSObject -Property @{
              HttpServer = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalMailboxServer]
              ExternalOwaUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalWebClientUrls].urls[0].url
              ExternalEcpUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEcpUrl]
              ExternalEwsUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl]
              ExternalOABUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalOABUrl]
              ExternalUMUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalUMUrl]
            }            
          }
        }                
      }
<#
    .SYNOPSIS
        This function uses the EWS Managed API to test the Exchange Autodiscover service.

    .DESCRIPTION
        This function will retreive the Client Access Server URLs for a specified email address
        by querying the autodiscover service of the Exchange server.

    .PARAMETER  EmailAddress
        Specifies the email address for the mailbox that should be tested.

    .PARAMETER  Location
        Set to External by default, but can also be set to Internal. This parameter controls whether
        the internal or external URLs are returned.
        
    .PARAMETER  Credential
        Specifies a user account that has permission to perform this action. Type a user name, such as 
        "User01" or "Domain01\User01", or enter a PSCredential object, such as one from the Get-Credential cmdlet.
        
    .PARAMETER  TraceEnabled
        Use this switch parameter to enable tracing. This is used for debugging the XML response from the server.    
        
    .PARAMETER  IgnoreSsl
        Set to $true by default. If you do not want to ignore SSL warnings or errors, set this parameter to $false.
        
    .PARAMETER  Url
        You can use this parameter to manually specifiy the autodiscover url.        

    .EXAMPLE
        PS C:\> Test-Autodiscover -EmailAddress administrator@uclabs.ms -Location internal
        
        This example shows how to retrieve the internal autodiscover settings for a user.

    .EXAMPLE
        PS C:\> Test-Autodiscover -EmailAddress administrator@uclabs.ms -Credential $cred
        
        This example shows how to retrieve the external autodiscover settings for a user. You can
        provide credentials if you do not want to use the Windows credentials of the user calling
        the function.

    .LINK
        http://msdn.microsoft.com/en-us/library/dd633699%28v=EXCHG.80%29.aspx

#>      
}


There are a few ways to use this Test-Autodiscover function. First, lets say that you are logged on to a domain workstation. Running the function with the following parameters will use your logged on credentials to perform an SCP look up and return the internal settings for the specified e-mail address:

Test-Autodiscover -EmailAddress administrator@uclabs.ms -Location internal

You can omit the -Location parameter, which by default is set to "External", and the function will use DNS to locate autodiscover and retrieve the external settings:

Test-Autodiscover -EmailAddress administrator@uclabs.ms

Finally, you might be testing this from the a machine outside your domain. Just create a PSCredential object using the Get-Credential cmdlet and assign it to the -Credential parameter of the function. For example, here's the result from testing autodiscover for an Exchange Online mailbox hosted through Office 365:

Test-Autodiscover -EmailAddress admin@uclabs.onmicrosoft.com -Credential $cred

In order to use this function you'll need a few things. First, if you haven't already, go download the EWS Managed API. You'll also need the .NET Framework 3.5.1 and PowerShell v2 installed. At that point, all you need to do is add the Test-Autodiscover function to your shell session and you're good to go.

{ 12 comments… read them below or add one }

Hakim August 24, 2011 at 10:41 pm

Thanks you Mike, nice article and also very nice tools that can help many administrator !

Reply

Mike Pfeiffer August 25, 2011 at 8:24 am

Thanks Hakim

Reply

Rasheedah February 20, 2012 at 8:22 am

Great article and tool..I finally got it to work but i am getting this:

Exception calling “GetUserSettings” with “2″ argument(s): “The Autodiscover service couldn’t be located.”
At C:\test-autodiscover.psm1:75 char:47
+ $response = $autod.GetUserSettings <<<< (
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Cannot index into a null array.
At C:\test-autodiscover.psm1:86 char:47
+ HttpServer = $response.Settings[ <<<< [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::Ex
ternalMailboxServer]
+ CategoryInfo : InvalidOperation: (ExternalMailboxServer:UserSettingName) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

I assume this is not configured on the server, yes?

Thanks!!

Reply

Rasheedah February 20, 2012 at 9:38 am

I just ran the test autoconfigu via outlook and no errors….I am thinking I did something wrong running the powershell..

Reply

Mike Pfeiffer February 20, 2012 at 2:27 pm

Did you supply credentials?

Test-Autodiscover -EmailAddress admin@uclabs.onmicrosoft.com -Credential $cred

Reply

archer August 23, 2012 at 2:11 pm

Great Job, Mike ( MVP) . Im running the script against a hosted exchange mailbox. First , was getting this error :
“Cannot find path ‘C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll’ because it does not exist.

Was able to modify the script using the loadfile function

” $dllpath = “C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll”
[void][Reflection.Assembly]::LoadFile($dllpath) ”
Now i couldnt get past this :
=======================

Exception calling “GetUserSettings” with “2″ argument(s): “The request failed. The remote server returned an error: (40
1) Unauthorized.”
At C:\Users\v-9saram\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:56 char:47
+ $response = $autod.GetUserSettings <<<< (
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Cannot index into a null array.
At C:\Users\v-9saram\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:67 char:47
+ HttpServer = $response.Settings[ <<<< [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::Ex
ternalMailboxServer]
+ CategoryInfo : InvalidOperation: (ExternalMailboxServer:UserSettingName) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Any inputs would be really appreaciated..

Reply

Mike Pfeiffer August 23, 2012 at 3:43 pm

Looks like an authentication issue. Did you provide creds when running the function? For example:

Test-Autodiscover -EmailAddress admin@uclabs.onmicrosoft.com -Credential $cred

Reply

Mike S. October 3, 2012 at 6:17 pm

Great article and tool Mike. I tried running it a few different way as you described using the -Location and -Credential parameter but I seem to be getting the same error no matter how I run it.

For example: I first open powershell and execute the following command to feed credentials for the script.
1. $cred=Get-Credential
2. I enter my credentials. In this case its my Office 365 credentials.
3. I dot source the script so that I can execute the function Test-Autodiscover
4. I execute the following command :
Test-Autodiscover -EmailAddress user@myfederatedo365domain.com -Credential $cred

I get this error:
Exception calling “GetUserSettings” with “2″ argument(s): “The Autodiscover service could not be located.”
At C:\Users\mike\Desktop\Test-Autodiscover.ps1:75 char:47
+ $response = $autod.GetUserSettings <<<< (
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Cannot index into a null array.
At C:\Users\mike\Desktop\Test-Autodiscover.ps1:86 char:47
+ HttpServer = $response.Settings[ <<<< [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::Ex
ternalMailboxServer]
+ CategoryInfo : InvalidOperation: (ExternalMailboxServer:UserSettingName) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

This happens no matter if I run it with -Location internal, -Location External. Same error. Can you provide some guideance. I appreciate it. Thanks.

Reply

Mike Pfeiffer October 20, 2012 at 3:33 pm

Since you’re using a vanity domain, make sure you have an autodiscover record in DNS for that domain. Without it, you’ll get that exact error.

Reply

Ondrej March 18, 2013 at 7:13 am

Hi,
whats the fastest way to check that supplied URL is correct? I want to use hard coded URL for connection, but I need to have some check. And if that check fails the script use autodiscovery for getting correct URL

Reply

Brent Quick April 2, 2013 at 11:15 am

So in trying to get this to work, I am seeing an error not previously mentioned.

Function is dot sourced and I am providing the correct credentials.

Test-Autodiscover -EmailAddress admin@O365domain.onmicrosoft.com -Credential (Get-Credential)

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:

Cannot index into a null array.
At line:85 char:13
+ New-Object PSObject -Property @{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray

Any idea’s? Thanks!

Reply

Mike Pfeiffer April 2, 2013 at 7:36 pm

Hi Brent,

Just tried a couple of things…it looks like you’ll get that when $autod.GetUserSettings() fails thanks to my lack of error handling :)

Try running it with tracing turned on

Test-Autodiscover -EmailAddress user@domain.com -Credential (Get-Credential) -TraceEnabled

I noticed that when I entered invalid creds and caused the code to fail that I would get the same “Cannot index into a null array.” With tracing enabled, you should see a 401 unauthorized if indeed the creds are no good. Otherwise, you should get something that will hopefully explain why it fails.

Reply

Leave a Comment

Previous post:

Next post: