Testing Exchange Autodiscover with PowerShell

Exchange Autodiscover is deceptively simple. Until it doesn’t work. Then, repairing autodiscover can be surprisingly challenging.

Microsoft does provide the Microsoft Connectivity Analyzer which can (among other things) give you detailed information about your autodiscover situation. However, it works “from the outside to the inside”. This PowerShell allows you to work “from the inside to the outside”.

The script below allows you to test autodiscover to a specific server. If it succeeds, but your overall autodiscover fails, then you have a far reduced set of things to investigate, in order to repair your autodiscover deployment. This script will allow you to test internally or externally (given that your firewall/router has the appropriate NAT forwarding/translation rules and DNS is properly configured). If an internal test succeeds, but an external test fails – this points you to load-balancer/firewall/router. If the internal test fails – then you may have several items to check. Read the source of the script below for the most common issues.

I am developing a script that works “the same way” as Outlook is documented to work for autodiscover – with more insight to the various steps. While I have it working today, it’s not a pretty script. 🙂 I will post it when it is more clean.

Enjoy.


###
### Test-AutoD.ps1
###
### This script performs a basic test of Exchange autodiscover for a given
### hostname.
###
### This script does NOT behave exactly the same as Outlook autodiscover or
### ExRCA.com autodiscover. At this writing, that script is still in
### development.
###
### Instead, given a particular hostname, this script determines what the
### specified host returns as an autodiscover response. If you have chosen
### your real autodiscover host, the responses will be the same.
###
### This script allows you to test autodiscover to a specific host. None
### of the Microsoft provided tools give you that flexibility.
###
### In general, you must specify either useDefaultCredentials OR specify
### all three of username, password, and emailAddress.
###
### If you specify the "mobile" argument, then the request sent to
### autodiscover is for Exchange ActiveSync, versus the request that is
### sent for Outlook.
###
### The usage for useDefaultCredentials is explained within the source
### of the script. Look below.
###
### If this script works, but your Exchange autodiscover does not, there
### are probably five primary reasons:
###
###	[1] Misconfigured permissions on the Autodiscover virtual directory
###	[2] Misconfigured (or missing) Service Connection Point (SCP)
###	[3] Misconfigured DNS for $hostname
###	[4] Misconfigured SRV record
###	[5] Misconfigured permissions on the reverse proxy
###
### This is not a list of all possible issues.
###
### Michael B. Smith
### michael at TheEssentialExchange dot com
### October, 2014
###
### No warranties, express or implied, are available. This script is offered
### "as is".
###
### I hope this script works for you. If it does not, please tell me. I
### will attempt to figure out what is going on. However - no promises.
###

Param(
    [string]$username     = $null,
    [string]$password     = $null,
    [string]$emailAddress = $null,
    [string]$hostname     = 'autodiscover.example.com',
    [switch]$mobile,
    [switch]$useDefaultCredentials
)

if( [String]::IsNullOrEmpty( $hostname ) )
{
	throw "You must provide a hostname"
	return
}

$uri = 'https://' + $hostname + '/Autodiscover/Autodiscover.xml'
'Autodiscover URI:'
' '
$uri
' '

if( -not ( $useDefaultCredentials -ne $null -and $useDefaultCredentials.IsPresent ) )
{
	if( [String]::IsNullOrEmpty( $username ) )
	{
		$str = Read-Host "Enter username for request"
		if( [String]::IsNullOrEmpty( $str ) )
		{
			throw "You must provide a username"
			return
		}

		$username = $str
	}

	if( [String]::IsNullOrEmpty( $password ) )
	{
		$str = Read-Host -AsSecureString "Enter password for $username"
		if( [String]::IsNullOrEmpty( $str ) )
		{
			throw "You must provide a password"
			return
		}

		$cred = New-Object System.Management.Automation.PSCredential( $username, $str )
		$password = $cred.GetNetworkCredential().Password

		$cred = $null
		$str  = $null
	}

	if( [String]::IsNullOrEmpty( $emailAddress ) )
	{
		$str = Read-Host "Enter email address for $username"
		if( [String]::IsNullOrEmpty( $str ) )
		{
			throw "You must provide an emailaddress"
			return
		}

		$emailAddress = $str
	}
}

Set-StrictMode -version 2.0 

if( $mobile )
{
	### a mobile autodiscover request returns far less data than an Outlook autodiscover request
	### a mobile device doesn't need much information to configure ActiveSync

	$autoDiscoverRequest = @"
				$emailAddress
				http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006
"@
}
else
{
	$autoDiscoverRequest = @"
				$emailAddress
				http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a
"@
}

$autoDiscoverRequest = $autoDiscoverRequest.Replace( "`t", '' )  ### remove tab characters from the autodiscoverrequest. Exchange doesn't like those tabs.

'This is the autodiscover request that will be submitted:'
' '
$autoDiscoverRequest
' '

$req = New-Object System.Net.WebClient
$req.Encoding = [System.Text.Encoding]::UTF8

if( $useDefaultCredentials )
{
	### this means use Kerberos or NTLM authentication. it will only work if the autodiscover
	### client is joined to the domain and the computer can build a connection to a DC (so,
	### the computer is on a LAN or connected via a VPN or connected via DirectAccess).

	$req.UseDefaultCredentials = $true
}
else
{
	### Basic authentication is VERY basic. The username and password are turned into base64 and
	### separated by a colon. That's all. Never use except with SSL!

	$auth = 'Basic ' + [System.Convert]::ToBase64String( [System.Text.Encoding]::UTF8.GetBytes( $username + ':' + $password ) )
	$req.Headers.Add( 'Authorization', $auth )
}

### autodiscover has knowledge about user-agents. i found that surprising.
### the user-agent below is for Office 2013 Pro with SP1

$req.Headers.Add( 'Content-Type', 'text/xml' )
$req.Headers.Add( 'User-Agent',   'Microsoft Office/15.0 (Windows NT 6.1; Microsoft Outlook 15.0.4641; Pro)' )

$webpage = $null

try
{
	$webpage = $req.UploadString( $uri, $autoDiscoverRequest )
}
catch
{
	throw $_
	return
}

'This is the autodiscover response:'
' '

if( $webpage -eq $null )
{
	write-error "Webpage response is empty."
	return
}

if( $webpage -is [System.String] )
{
	$webpage
}
else
{
	$webpage.InnerXml
}

$webpage = $null
$req     = $null

' '
'Done'

Follow me on twitter @essentialexch

 

Leave a Reply

Your email address will not be published. Required fields are marked *