Category Archives: Microsoft Exchange

ADFS 4.0 on Server 2016 <-> Outlook Web App 2013

I recently enabled SAML authentication on Outlook Web App 2013, following the TechNet Documentation here: https://technet.microsoft.com/en-us/library/dn635116(v=exchg.150).aspx

It seemed to work fine; however, I would occasionally (about 1/8 attempts) receive an error message saying: “WrongAudienceUriOrBadSigningCert”

I had already added my ADFS token signing certificate to the Exchange server’s trusted root store as  suggested here:  https://flamingkeys.com/exchange-2013-with-ad-fs-login-fails-with-wrongaudienceuriorbadsigningcert/

The truly troubling thing was, that the issue could not be reproduced reliably.  It affected both internal and external devices (both the primary ADFS and the ADFS Web Application Proxy servers)

I watched a fiddler trace as I attempted to access OWA, and the only difference between successful and failed attempts was a  “/” at the end of the URL.

This can be observed in the POST body of the 302 to owa:


<saml:AudienceRestrictionCondition>
<saml:Audience>https://mail.corp.org/owa/</saml:Audience>
</saml:AudienceRestrictionCondition>

<saml:AudienceRestrictionCondition>
<saml:Audience>https://mail.corp.org/owa</saml:Audience>
</saml:AudienceRestrictionCondition>

This is the token issued to me by my ADFS4 Server!  It would seem that the tokens issued by the IdP do not contain a consistent Audience tag.

The TechNet documentation states very clearly that

The inclusion of the trailing slash / in the URL examples shown below is intentional. It’s important to ensure that both the AD FS relying party trusts and Exchange Audience URI’s are identical. This means the AD FS relying party trusts and Exchange Audience URI’s should both have or both emit the trailing slashes in their URLs. The examples in this section contain the trailing /’s after any url ending with “owa” ( /owa/) or “ecp” (/ecp/).

Ignoring this advise, I added all 4 urls to my Exchange farm configuration


$uris = @("https://mail.corp.org/owa/","https://mail.corp.org/ecp/","https://mail.corp.org/owa","https://mail.corp.org/ecp")

Set-OrganizationConfig -AdfsIssuer "https://adfs.corp.org/adfs/ls/" -AdfsAudienceUris $uris -AdfsSignCertificateThumbprint "<thhumb>"

Having 4 audience URIs resulted in a 100% success rate while attempting to open OWA from a successful ADFS authentication.

I hope this helps someone, as I couldn’t seem to find this issue anywhere else online.

Exchange Dynamic Distribution Group Delivery Problems

Consider the following:

An Exchange Dynamic Distribution Group has a valid recipient filter, and the filter generates the desired resultant set of recipients with the following PowerShell command:

Get-Recipient - RecipientPreviewFilter $(Get-DynamicDistributionGroup "name").RecipientFilter

However, when a user sends a message to the group, no messages are delivered, and the sender does not receive an NDR.

One possible cause of this issue is a property of the dynamic distribution group called RecipientContainer.  This is similar to the SearchBase attribute of the Get-ADUser cmdlet: it specifies the container in which to apply the RecipientFilter.  Therefore, the RecipientContainer must be the OU (Or a parent of) in which the desired users are stored.

More info here: https://www.corelan.be/index.php/2008/11/05/dynamic-distribution-lists-not-working-as-expected-0-recipients-during-mail-routing/

Pushing Calendar Events with the EWS API

We’ve had a need to populate users’ calendars with data from an internal FileMaker Database, so I dug around in the EWS API, and came up with a script that uses the FileMaker ODBC Connection, and the EWS API to accomplish the task:

First things first, we need to install the EWS Managed API on the machine that will run the script.

After the EWS Managed API is installed, we need to reference it in our PowerShell script:

Add-Type -Path “C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll”

Next, we need to set up a System.Net.NetworkCredential object for the account we’ll use to push these events.  This account must have at least modify permission on the target users’ calendar.

$Credentials = new-object system.net.NetworkCredential(“CalendarAccessAccount”,”SuperStrongPa$$w0Rd!”,”litware”)

Next, we need to Create anMicrosoft.Exchange.WebServices.Data.ExchangeService object:

$version = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService($version)

We don’t want to use the default credentials, Instead we want to authenticate using the service account specified earlier:

$service.UseDefaultCredentials = $false
$service.Credentials=$Credentials

And, presuming AutoDiscover is set up correctly in our domain, we want to let EWS figure out the server address, port, etc:

$service.AutodiscoverUrl(“TargetMailbox@litware.com”)

Next, we need to reference the user’s calendar (it’s really just a folder as far as the API is concerned):

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, “TargetMailbox@litware.com”)

And finally, we build the appointment object:

$Appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $Service
$appointment.Subject = “Test111”
$appointment.Body = “Test111”
$appointment.Start = $(Get-Date).AddHours(6)
$appointment.End =$(Get-Date).AddHours(9)

Don’t forget to save it:

$appointment.Save($folderid)

 

All in all, we can wrap this up as a function:

Function CreateAppointment($User,$Credentials)
{
$mailboxName=$User
$version = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService($version)
$service.UseDefaultCredentials = $false
$service.Credentials=$Credentials
$service.AutodiscoverUrl($mailboxName)

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, $mailboxName)

$Appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $Service
$appointment.Subject = “Test Subject”
$appointment.Body = “Test Body”
$appointment.Start = $(Get-Date).AddHours(6)
$appointment.End =$(Get-Date).AddHours(9)

$appointment.Save($folderid)

}

Now we can call the function from, say, with a loop so as to iterate through each user in a CSV:

$users = Import-CSV “Users.csv”

$Credentials = new-object system.net.NetworkCredential(“CalendarAccessAccount”,”SuperStrongPa$$w0Rd!”,”litware”)

Foreach ($User in $Users)

{

CreateAppointment $User $Credentials

}

 

More to come later on the FileMaker ODBC Connection…

Cleaning Up Exchange Messages with Search-Mailbox

Like most sysadmins, I receive notifications from end users about SPAM showing up in their inbox.  While not all spam can be avoided, we can deal with it.  I wanted to lessen the impact of already delivered spam and potentially avert a crisis if the same phishing email is sent to all 1500 mailboxes, so I whipped up this script to search out and destroy these messages from my Exchange environment:

$Subject = “About your last transaction”
$StartDate = $(‘1/1/2015’)
$BodyLanguage = “sellam.fr”
$TargetMailbox = “spamdump”
$TargetFolder = “WHD2918”

$Search = [scriptblock]::Create(“Received>=`”$StartDate`” and Subject:`”$Subject`” and `”$BodyLanguage`””)

Get-Mailbox -ResultSize Unlimited | Search-Mailbox -SearchQuery $Search -targetmailbox $TargetMailbox -targetfolder $TargetFolder -loglevel full -logonly

Note the last flag in the last line of the script: “-logonly.”  Be very careful to run the command with this command the first go-round.  This ensures that the query you specify does not grab messages that it shouldn’t (and you wind up deleting everyone’s entire mailbox).  The result of logonly is an excel file in the target mailbox with the headers of the resultant messages.

After reviewing the messages, replace -logonly with -deletecontent.  This will actually move the messages from the users’ mailboxes into the target mailbox.

If you want to modify the query, take a look into how Search-Mailbox actually works.   Search-Mailbox uses KQL, so be sure to brush up on the syntax.  If you’ve beocme accustomed to the powershell boolean operators such as “-and,” You’ll be unpleasantly surprised when you learn that the same operator will evaluate to “not and” in KQL