Understanding Outlook Auto-Mapping

Auto-mapping is an Exchange & Exchange Online feature, which automatically opens mailboxes with Full Access permissions in a delegate’s Outlook client. The setting is configurable by an Administrator when Full Access permissions are assigned for a user. Once enabled, the periodic Autodiscover requests from the Outlook client will determine which mailboxes should be mapped for a user. Any auto-mapped mailboxes with be opened by the Outlook client in a persistent state and cannot be closed by the user. If users want to remove the auto-mapped mailbox from their Outlook client, Administrative intervention is required to remove the Full Access permission, or clear the auto-mapping flag.

There are two scenarios where you may want to use this configuration:

  • You would like mailboxes to automatically open & persist in Outlook, for users who are assigned full access permissions
  • When users require access an Online Archive associated with the delegating mailbox – the Online Archive isn’t available if you add the mailbox via the “Open these additional mailboxes” console in a mailbox profile. Note that the Online Archive is also available via “Open Another Users Mailbox in Outlook Web App and in some cases, when the delegating mailbox is added as an additional mailbox in the Outlook profile (this has proven to be unreliable depending on identity & permission assignment.)

The auto-mapping feature may be undesirable for users who have access to a large number of mailboxes (taking up valuable Outlook screen real estate), or for users who want to control which mailboxes are open in their Outlook client.

If we take a look at the Autodiscover response for my Office 365 mailbox, we can see the AlternativeMailbox options that advertise a Shared Mailbox (including its Online Archive) for which I have Full Access permissions & auto-mapping enabled.

<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
...
<AlternativeMailbox>
<Type>Delegate</Type>
<DisplayName>! Shared Mailbox</DisplayName>
<SmtpAddress>sharedmailbox@kloud.com.au</SmtpAddress>
<OwnerSmtpAddress>sharedmailbox@kloud.com.au</OwnerSmtpAddress>
</AlternativeMailbox>
<AlternativeMailbox>
<Type>Archive</Type>
<DisplayName>In-Place Archive - ! Shared Mailbox</DisplayName>
<SmtpAddress>ExchangeGuid+b44be8a9-61b3-4cb4-a1f5-0b845b1fb419@kloud.mail.onmicrosoft.com</SmtpAddress>
<OwnerSmtpAddress>sharedmailbox@kloud.com.au</OwnerSmtpAddress>
</AlternativeMailbox>
...
</Account>
</Response>
</Autodiscover>

Auto-Mapping Principles

  • By default, auto-mapping is enabled whenever Full Access permissions are granted to the mailbox via Add-MailboxPermission. It must be explicitly set to false if you want it disabled for a user i.e -Automapping:$false
  • At this time, it is not possible to view the state of the auto-mapping setting for a mailbox (whether it has been enabled/disabled)
  • If you are using Mail-Enabled Security Groups to grant Full Access permissions, auto-mapping will not work for the group members. The auto-mapping feature must be assigned by granting Full Access to individual user objects

Managing the Setting

It is enabled per-user, for new or existing Full Access delegates:

Add-MailboxPermission sharedmailbox@kloud.com.au -AccessRights FullAccess -User david.ross@kloud.com.au -Automapping:$true

You can disable auto-mapping for a single user, by removing the user’s Full Access permission and then reinstating the permission with the -automapping:$false parameter defined:

Remove-MailboxPermission sharedmailbox@kloud.com.au -AccessRights FullAccess -User david.ross@kloud.com.au
Add-MailboxPermission sharedmailbox@kloud.com.au -AccessRights FullAccess -User david.ross@kloud.com.au -Automapping:$false

In Exchange Online, auto-mapping can be removed for all existing Full Access delegates on a mailbox by running the command:

Remove-MailboxPermission sharedmailbox@kloud.com.au -ClearAutoMapping

 

That’s it, simple! Hopefully this helps to explain the nuances of Outlook auto-mapping.

WPAD and Proxy Auth Cause Exchange HCW to Fail

A recent conversation with a colleague reminded me of an issue I’ve faced a number of times (and forgotten to blog about) when running the Exchange Hybrid Configuration Wizard (HCW) on Exchange 2010 or 2013 in an environment where Web Proxy Autodiscovery Protocol (WPAD) is used.

The Problem

The most common scenario where I’ve seen this come into play is along the lines of this:

  1. WPAD is used to distribute Proxy.PAC to client machines
  2. Customer permits direct connection from Exchange servers to Internet
  3. From an elevated command prompt, run “netsh winhttp reset proxy” to ensure a direct connection
  4. Change Internet Options settings from “Automatically detect settings” to “Disabled”
  5. Browse to a site restricted by the proxy to confirm proxy bypass is working
  6. Can connect to Exchange Online using Remote PowerShell
  7. Run the HCW but it fails with the following error in the logs:
    ERROR : System.Management.Automation.RemoteException: Federation information could not be received from the external organization.
    ERROR : Subtask NeedsConfiguration execution failed: Configure Organization Relationship
    Exchange was unable to communicate with the autodiscover endpoint for your Office 365 tenant. This is typically an outbound http access configuration issue. If you are using a proxy server for outbound communication, verify that Exchange is configured to use it via the “Get-ExchangeServer –InternetWebProxy” cmdlet. Use the “Set-ExchangeServer –InternetWebProxy” cmdlet to configure if needed.

At this point, you’d probably be scratching your head wondering where it’s going wrong. I certainly was. It can be difficult to troubleshoot issues with the HCW as it is over 50 configuration steps all wrapped up in a ‘friendly’ GUI. The first error gives a good indication of where to look: Federation information could not be received from the external organization.

Check whether federation information can be retrieved from Office 365 by running the cmdlet from your Exchange server:

Get-FederationInformation –DomainName YourTenant.mail.onmicrosoft.com - BypassAdditionalDomainValidation: $true -Verbose

In my case it failed and I found this little gem in the verbose output:

Exception=The remote server returned an error: (407) Proxy Authentication Required

So where is it getting this proxy configuration from? As it turns out, the HCW (and the individual cmdlets) actually run in the user context of the Local System account, rather than the current user. The default configuration for Internet Options is for “Automatically Detect Settings” to be enabled on a per-user basis, which also applies to the Local System account. This default setting combined with WPAD and client’s PAC file is directing the HCW out through the proxy server, which is always going to fail given that Local System is not a credential that the proxy server will validate.

The Workarounds

There are a few ways to address this, although the first two may be harder to implement if it’s a larger organisation:

  • Permit the server to use the proxy without authentication
  • Modify the PAC file settings being distributed by WPAD

Alternatively it is possible to change the default Internet Options settings within the Local System profile:

  1. In your own profile, configure the default Internet Option setting from “Automatically Detect Settings” to “Disabled” and export the registry key from HKCU. Import this key into the “Local System” (HKEY_USERS\.DEFAULT) hive. Always remember to back up your registry before making changes.
    HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections
  2. Use PsExec to launch Internet Explorer as “Local System”, disable the setting and save the changes
    psexec.exe -i -s -d “C:\Program Files\Internet Explorer\iexplore.exe”

Exchange Server hybrid “edition” myths and misunderstandings

Originally posted on Lucian’s blog at clouduccino.com (click here to view). Follow Lucian on Twitter @LucianFrango.


There’s a common misunderstanding that Exchange Server hybrid (whichever version you may be running) is needed to be kept on-premises forever if you have Azure AD Connect. AADC syncs on-premises Active Directory with Azure AD. When AADC and federated identity is enabled, MOST of the cloud attributes in Azure AD are READ ONLY. From that statement it’s been understood that hybrid is needed to be maintained to do all that Exchange Online remote management goodness. Wrong!

I hate to burst the bubble here, but, I’m going to burst the bubble.

Exchange Server Hybrid

Being a consultant, I’m going to do that frustrating thing and say those famous words: “it depends on the situation”. I love being ambiguous sometimes as it affords room for different options and ideas which is great for brainstorming and architecting.

Looking at Exchange Server hybrid functionality independently of thinking about the common tech phrase “hybrid”, what does Exchange Server hybrid do? Put simply, which isn’t very clear on TechNet or other publications, hybrid creates send and receive connectors between on-premises and Office 365 EXO. It’s now just a wizard / setup application that completes a few commands that can be achieved manually through powershell. It’s not even an Exchange Server role anymore.

Read More

Consuming CSV files from an Exchange Mailbox via Exchange Web Services and FIM/MIM 2016 using the Granfeldt PowerShell MA

This solution on first look is quite random. A management agent that consumes a flat file (comma separated file) isn’t ground breaking, but when the twist is that the CSV file is in an email in an Exchange Inbox, it’s quite a different scenario.

Background

My customer uses a Cloud Service for their recruitment processes. The cloud service does have a SOAP API that I could potentially develop a FIM/MIM solution for using the Microsoft Web Services Management Agent, however my customer does not have API access to their tenant, the vendor isn’t overly responsive and I need a solution in days not weeks.

On the upside, my customer can quickly create reports in the SaaS Portal, and schedule them to be delivered (via CSV/Excel) to an email address. So, what if I was able to integrate FIM/MIM to the inbox that receives the emails with attached reports that contain the information I require and process it accordingly? This blog post is that solution.

Overview

Once a day there is a scheduled process that generates a report (CSV) of new staff from a SaaS provider. That CSV is emailed to an Inbox we created to receive these reports. Using the Granfeldt PowerShell Management Agent I created a solution that;

  • Connects to the specified Exchange Mailbox using Exchange Web Services
    • Enumerates the inbox looking for emails with attachments
    • Validates the emails with attachments by looking for the sender and attachment type we are expecting
    • Extracts the attachment to a file share
    • Moves all messages with attachments to a Processed subfolder
  • Processes the most recent report attachment (CSV) (in case the MA hasn’t run for few days or the reports start coming more than once a day) or if there is no new email message with attachment in the inbox, processes the most recent attachment we previously put in the file share
    • Each report is cumulative so the MA logic stays simple
  • Imports to MIM the new staff that are due to start in the next 7 days (to allow for all access to be setup prior to their first day of employment) and kicks off the MIM Provisioning processes
    • Triggers entitlements and access through the system accordingly (not covered in this post, but includes provisioning of mailbox, home directory, group memberships etc)

Notes:

  • The MIM Synchronisation Service Account will need access permissions to save files into the File Share
  • The MIM Server and this PSMA will require the Microsoft EWS 2.2 API to be installed on the MIM Synchronisation Server. It is available from here https://www.microsoft.com/en-us/download/details.aspx?id=42951

Getting Started with the Granfeldt PowerShell Management Agent

First up, you can get it from here. Søren’s documentation is pretty good but does assume you have a working knowledge of FIM/MIM and this blog post is no different.

Three items I had to work out that I’ll save you the pain of are;

  • You must have a Password.ps1 and Export.ps1 file. Even though we’re not doing password management, or exporting back to the SaaS Provider on this MA, the PS MA configuration requires a file for these fields. The .ps1 doesn’t need to have any logic/script inside it. It just needs to be present.
  • The credentials you give the MA to run the scripts as, needs to be in the format of just ‘accountname’ NOT ‘domain\accountname’. I’m using the AD Account associated with the Exchange Mailbox that receives the emails with the CSV reports.
  • The path to the scripts in the PS MA Config must not contain spaces and be in old-skool 8.3 format. I’ve chosen to store my scripts in an appropriately named subdirectory under the MIM Extensions directory. Tip: from a command shell use dir /x to get the 8.3 directory format name. Mine looks like C:\PROGRA~1\MICROS~4\2010\SYNCHR~1\EXTENS~2\PageUp

Schema.ps1

My schema is essentially the columns that are in the CSV report that I’m importing.

Password Script (password.ps1)

Empty as described above

Import.ps1

Connect to the Exchange Mailbox, find messages from the defined user sending them where the attachment is of the expected naming and format. Extract the CSV file to a File Share. Move emails with attachments to a processed folder. Parse the CSV, perform some logic on the data and import objects and values for new employees.

Export.ps1

Empty as we’re not writing anything back to the SaaS provider.

Wiring it all together

In order to wire the functionality all together there are the usual number of configuration steps to be completed. Below I’ve shown a number of the key points associated with making it all work. This is all Synchronisation Engine MA configuration tasks. Basically create the PS MA, import attributes from the PS MA, create your MA Run Profiles and let it loose.

Management Agent Configuration

As per the tips above, the format for the script paths must be without spaces etc. I’m using 8.3 format and I’m using the same service account as my AD MA.

Password and Export scripts must be specified but as we’re not doing password management or exporting they’re empty as detailed above.

If your schema.ps1 file is formatted correctly, you can select your attributes/columns that will be coming in from the CSV file.

My join rule is simple. StaffID to AccountName in the MetaVerse.

My import flows are direct flows with a Boolean flag to kick off a bunch of declarative rules out of the Portal.

Summary

Thinking outside of the box and using the Granfeldt PowerShell MA I was able to quickly consume a CSV file from an Exchange Inbox to kick off the provisioning process.

Follow Darren on Twitter @darrenjrobinson

Delegate Mailbox Access using Groups in Exchange Online

A common misconception about granting mailbox access rights in Exchange Online is that you can only add access to the individual and not a group. You may have opened the Exchange Administrator Center (EAC), found the mailbox you wanted and looked at the delegated access tab. Only to be provided with a list of eligible user identity’s, but none of your on-premises security groups that have been created. Fear not, the on-premises groups just need a little remediation to the correct flavour to be seen in the picker and then applied.
The key settings of a group to assign mailbox access in Exchange Online are:
  1. Universal Security
  2. Mail Enabled
Things in your environment may have gotten this way over time as legacy on-premises Exchange Server versions weren’t as picky with your selection and would allow the permission to be granted to any kind of group you threw at it. Moving to Exchange Online has more sophisticated taste that needs to be catered for due to its Evergreen state with all the latest Exchange trends.
If you migrate on-premises mailboxes with these slightly incorrect permissions to Exchange Online they will be lost forever. So if you remedy the existing on-premises mailboxes before initiating a remote move request to Exchange Online, no further action will be required at completion.

The Process

Update the group to universal:
Get-Group $Name | Set-Group -Universal
Mail enable the group:
Enable-DistributionGroup -identity $Name
Finally, not compulsory but if you want the group to be hidden from your Exchange Address Book:
Get-DistributionGroup -identity $Name | Set-DistributionGroup -HiddenFromAddressListsEnabled:$True
Force a synchronisation of your group modifications with Azure Active Directory and you should find that the group will be available in the Office 365 Exhange Administrator Center. On the Azure AD Connect Server:
CD C:\Program Files\Microsoft Azure AD Sync\Bin\
.\DirectorySyncClientCmd.exe delta
So my final note that I would like to re-iterate is that if you tidy this stuff up before migrating to Office 365 you will have a lot less problems when the mailbox becomes active in Office 365. A mailbox that has been migrated with a faulty group type will have permissions assigned on the object, but will not resolve to an object and therefore only show SID information. Firstly the permissions fail, but secondly, it makes it very difficult to find out what the group was previously to add post migration. (see first permissions below)

 Hot Tip

Fix the issue with your groups before you start the synchronisation of the mailbox to Exchange Online and the permissions will be stamped correctly during the initial phases of the move request and once finalised, will work as expected.

Provision Users for Exchange with FIM/MIM 2016 using the Granfeldt PowerShell MA, avoiding the AD MA (no-start-ma) error

Forefront / Microsoft Identity Manager provides Exchange Mailbox provisioning out of the box on the Active Directory Management Agent. I’ve used it in many many implementations over the years. However, in my first MIM 2016 implementation in late 2015 I ran into issues with something I’d done successfully many times before.

I was getting “no-start-ma” on the AD MA on export to AD. The point at which the MA sets up its connection to the Exchange environment. After some searching I found Thomas’s blog detailing the problem and a solution. In short update the MIM Sync Server to .NET 4.6. For me this was no-joy. However when MS released the first rollup update for MIM in December everything fired up and worked as normal.

Step forward a month as I was finalising development for the MIM solution I was building for my customer and my “no-start-ma” error was back when I re-enabled mailbox provisioning. Deselect the Exchange Provisioning option on the AD MA and all is good. Re-enable it and it fails. One week left of dev and I need mailbox provisioning so time for a work around whilst I lodge a Premier Support ticket.

So how can I get mailbox provisioning working reliably and quickly? I was already using Søren Granfeldt’s PowerShell MA for managing users Terminal Services configuration, Home Directories and Lync/Skype for Business. What’s one more. Look out for blog posts on using the PS MA to perform those other functions that I’ll be posting in the coming weeks.

Using the Granfeldt PowerShell Management Agent to Provision Exchange Mailboxes

In this blog post I’ll document how you can enable Mailbox Provisioning in Exchange utilising Søren Granfeldt’s extremely versatile PowerShell Management Agent. I’ll show you how to do the minimum of enabling a user with a mailbox. Understanding how this is done you can then easily then extend the functionality for lifecycle management (e.g. change account settings for POP/IMAP/ActiveSync and de-provisioning).

My Exchange PS MA is used in conjunction with an Active Directory MA and Declarative Provisioning Rules in the MIM Portal. Essentially all the AD MA does, when you enable Exchange Provisioning (when it works) is call the ‘update-recipient’ cmdlet to finish of the mailbox provisioning. My Exchange PSMA does the same thing.

Overview

There are three attributes you need to supply values for in order to then provision them a mailbox (on top of having an Active Directory account, or course);

  • mailNickName
  • homeMDB
  • homeExchangeServerName

The later two I’m flowing the appropriate values for using my Active Directory MA. I’m setting those attributes on the AD MA as I’m provisioning the AD account on that MA which then lets me set those two attributes as initial flow only. I’m doing that as over time it is highly likely that those attribute values may change with normal business as usual messaging admin tasks. I don’t want my Exchange MA stomping all over them.

Getting Started with the Granfeldt PowerShell Management Agent

First up, you can get it from here. Søren’s documentation is pretty good but does assume you have a working knowledge of FIM/MIM and this blog post is no different. Configuration tasks like adding additional attributes the User Object Class in the MIM Portal, updating MPR’s, flow rules, Workflows, Sets etc are assumed knowledge and if not is easily Bing’able for you to work it out.

Three items I had to work out that I’ll save you the pain of are;

  • You must have a Password.ps1 file. Even though we’re not doing password management on this MA, the PS MA configuration requires a file for this field. The .ps1 doesn’t need to have any logic/script inside it. It just needs to be present
  • The credentials you give the MA to run the scripts as, needs to be in the format of just ‘accountname’ NOT ‘domain\accountname’. I’m using the service account that I’ve used for the Active Directory MA. The target system is the same directory service and the account has the permissions required (you’ll need to add the management agent account to the appropriate Exchange role group for user management)
  • The path to the scripts in the PS MA Config must not contain spaces and be in old-skool 8.3 format. I’ve chosen to store my scripts in an appropriately named subdirectory under the MIM Extensions directory. Tip: from a command shell use dir /x to get the 8.3 directory format name. Mine looks like C:\PROGRA~1\MICROS~4\2010\SYNCHR~1\EXTENS~2\Exchange

Schema Script (schema.ps1)

As I’m using the OOTB (out of the box) Active Directory MA to provision the AD account and only showing mailbox provisioning, the schema only consists of the attributes needed to know the state of the user with respect to enablement and the attributes associated with enabling and confirming a user for a mailbox.

https://gist.github.com/darrenjrobinson/ae46cdfccb825dce69b3

Password Script (password.ps1)

Empty as described above.

Import Script (Import.ps1)

Import values for attributes defined in the schema.

Export Script (Export.ps1)

The business part of the MA. Take the mailnickName attribute value flowed from FIM, (the other required attributes are populated via the AD MA) and call update-recipient to provision the mailbox.

Wiring it all together

In order to wire the functionality all together there are the usual number of configuration steps to be completed. Below I’ve shown a number of the key points associated with making it all work.

Basically create the PS MA, import attributes from the PS MA, add any additional attributes to the Portal Schema, update the Portal Filter to allow Administrators to use the attribute, update the Synchronisation MPR to allow the Sync Engine to flow in the new attribute, create the Set used for the transition, create your Synchronisation Rule, create your Mailbox Workflow, create your Mailbox MPR, create your MA Run Profiles and let it loose.

Management Agent Configuration

As per the tips above, the format for the script paths must be without spaces etc. I’m using 8.3 format and I’m using the same service account as my AD MA.

Password script must be specified but as we’re not doing password management its empty as detailed above.

If your schema.ps1 file is formatted correctly you can select your attributes.

My join rule is simple. AccountName (which as you’ll see in the Import.ps1 is aligned with sAMAccountName) to AccountName in the MetaVerse.

My import flows are a combination of logic used for other parts of my solution, a Boolean flag & Mailbox GUID to determine if the user has a mailbox or not (used for my Transition Set and my Export script).

Below is my rules extension that sets a boolean value in the MV and then flowed to the MIM Portal that I use in my Transition Set to trigger my Synchronisation Rule.

Synchronisation Rules

My Exchange Outbound Sync rule doesn’t and isn’t complex. All it is doing is sync’ing out the mailnickName attribute and applying the rule based on an MPR, Set and Workflow.

For this implementation my outbound attribute flow for mailnickName is a simple firstname.lastname format.

Set

I have a Set that I use as a ‘transition set’ to trigger provisioning to Lync. My Set looks to see if the user account exists in AD (I flow in the AD DN to an attribute in the Portal) and the mailbox status (set by the Advanced Flow Rule shown above). I also have (not shown in the screenshot) a Boolean attribute in the MIM Portal that is set based on an advanced flow rule on the AD MA that has some logic to determine if employment date as sourced from my HR Management Agent is current and the user should be active or not).

Workflow

An action based workflow that will use the trigger the Synchronisation rule for Exchange Mailbox creation.

MPR

Finally my MPR for provisioning mailboxes is based on the transition set,

and my Mailbox Workflow.

Summary

Using the Granfeldt PowerShell MA I was able to quickly abstract Mailbox Provisioning from the AD Management Agent and perform the functionality on its own MA.

 

Follow Darren on Twitter @darrenjrobinson

Exchange 2013 DNS Settings Cause Transport Services to Crash

I ran into a problem at a customer recently with two Exchange 2013 servers where the ‘Microsoft Exchange Frontend Transport’ (MSExchangeFrontEndTransport) service would crash continually. It would eventually bring down the ‘Microsoft Exchange Transport’ (MSExchangeTransport) and ‘Microsoft Exchange Mailbox Transport Submission’ (MSExchangeSubmission) services. This meant the server was responding to SMTP connections with ‘451 4.7.0 Temporary server error. Please try again later. PRX2’ on attempting to submit a message.

The primary error in the Event Log was Event ID 1000:

Faulting application name: MSExchangeFrontendTransport.exe, version: 15.0.712.0, time stamp: 0x5199c77c
Faulting module name: Microsoft.Exchange.Net.ni.dll, version: 15.0.712.14, time stamp: 0x51b4dcae
Exception code: 0xc00000fd

Kloud blog Exchange 2013 Crash 2

The Exchange 2013 Health Service would try to recover the service, it would keep crashing, the Windows Problem Reporting service used up all the CPU and memory and the server would crash.

Kloud blog Exchange 2013 Crash 3

We eventually discovered the problem was with the IPv4 DNS settings. Because this organisation did not use dynamic DNS registration and statically registered DNS entries in BIND, the settings for ‘Register this connection’s addresses in DNS’ and ‘Use this connection’s DNS suffix in DNS registration’ were disabled as can be seen below. Enabling these again allowed the services to start immediately.

Kloud blog Exchange 2013 Crash 1

I have replicated this problem on Exchange 2013 CU2-v2 (build 15.0.712.24) and Exchange 2013 RTM build 15.0.516.32 running on Windows 2012. In a test of a single Windows 2008 R2 server it did not show the same problem.

I am not sure if this is a Windows 2012 bug or an Exchange bug, but a combination of these two are not playing nicely together with DNS registration disabled. The only reference I could find to this was a TechNet forum post

http://social.technet.microsoft.com/Forums/exchange/en-US/fc23776c-bae4-4ca9-ad6d-4f8df880f47c/451-470-temporary-server-error-please-try-again-later-prx2?forum=exchangesvrsecuremessaging

WAP Exchange and Lync Applications

PowerShell Deployment of Web Application Proxy and ADFS in Under 10 Minutes

===========================================================================
Updated 10 September 2013: tested with Windows 2012 R2 RTM and the script functions as in R2 Preview. Outlook Anywhere bug in the Preview code has been fixed and Outlook now works with RTM. Updated the script to correct Autodiscover ExternalURL
===========================================================================

In this post I will be discussing deploying a highly available Windows 2012 R2 Preview ADFS and Web Application Proxy solution using only PowerShell. This was done as a proof of concept to compare the time taken as well as complexity to build and configure a Reverse Proxy solution to replace a UAG 2010 array. Those of you who have worked with UAG will know that just installing and applying UAG updates and Service Packs takes over an hour – before you even get to the application configuration which is totally manual. For a client recently with 6 trunks publishing Exchange, Lync, Office Web Apps, FIM and SharePoint with the same applications published on multiple domain names it would take over 8 hours of mind numbing, RSI inducing and error prone manual input. The configuration I have built here is for Exchange 2013, Lync 2013 and Office Web Apps Server only, but adding SharePoint and FIM would add about 30 seconds to the installation time.

The other purpose of these scripts is to get something that is repeatable and easily configurable which gives certainty of the configuration and avoids human error. Note that this is a proof of concept so I have not put in much error checking – all code should be tested in a lab environment.

The end result on the ADFS side is an ADFS farm with two servers and a Non-Claims-Aware Relying Party Trust for Exchange.

ADFS Relying Party Trusts 2

And on the Web Application Proxy side we have a cluster of servers with two ADFS preauthenticated applications and 10 Pass-through applications.

WAP Exchange and Lync Applications

Background Reading

These scripts are based on information, functions and script extracts from some of my previous blog posts so if you want more information and background then I recommend you review these:

Windows 2012 R2 Preview Web Application Proxy – Exchange 2013 Publishing Tests for the Web Application Proxy overview

Publish Lync 2013 with 2012 R2 Preview Web Application Proxy for the Lync configuration and SNI requirements

SSL SAN Certificate Request and Import from PowerShell for the PowerShell based certificate requests

Outlook Anywhere NTLM SSO with UAG 2010 KCD for the KCD configuration

Overview of the Scripts

For ease of installation I have split this into four PowerShell scripts which do the following:

Setup-ADFSserver1.ps1 which is run on the first internal ADFS server
  1. User input of a password for exporting the ADFS certificate to .PFX with private key
  2. User input of the credentials for the ADFS service account
  3. Either import the ADFS certificate from a PFX file, or if used for testing – generate a certificate request .ini file for ADFS, requests a certificate from an online CA and exports the certificate as a PFX file to a file share
  4. Install an ADFS farm using WID
  5. Add a non claims aware relying party trust for Exchange using a permit all issuance rule
Setup-ADFSserver2.ps1 which is run on the secondary ADFS server
  1. User input of the password for importing the ADFS certificate
  2. User input of the credentials for the ADFS service account
  3. Import the ADFS certificate
  4. Add the server as an additional node to the ADFS farm
Setup-WAPserver1.ps1 which is run on the first Web Application Proxy server
  1. User input of the password for importing the ADFS certificate
  2. User input of a password for exporting the Web Application Proxy certificate to .PFX with private key
  3. User input of the credentials for an administrator of the ADFS server (not the ADFS service account)
  4. Import the ADFS certificate
  5. Either import the Web Application Proxy certificate from a PFX file, or if used for testing – generate a certificate request .ini file for the Web Application Proxy, requests a certificate from an online CA and exports the certificate as a PFX file to a file share
  6. Install the Web Application Proxy and add it as an ADFS proxy
  7. Add application publishing rules for Lync and Office Web Apps Server
  8. Add application publishing rules for Exchange
  9. Add SPN for KCD (Kerberos Constrained Delegation) to allow the WAP server to delegate credentials for the Exchange server
  10. Set the default certificate binding for clients that do not support SNI (Server Name Indication)
Setup-WAPserver2.ps1 which is run on the secondary WAP server
  1. User input of the password for importing the ADFS certificate
  2. User input of the password for importing the Web Application Proxy certificate to .PFX with private key
  3. User input of the credentials for an administrator of the ADFS server (not the ADFS service account)
  4. Import the ADFS certificate
  5. Import the WAP certificate
  6. Install the Web Application Proxy and add it as an ADFS proxy
  7. Add SPN for KCD (Kerberos Constrained Delegation) to allow the WAP server to delegate credentials for the Exchange server
  8. Set the default certificate binding for clients that do not support SNI (Server Name Indication)

In my testing the first ADFS server took on average 2 minutes 15 seconds, the second ADFS server 2 minutes 15 seconds, the first WAP server 2 minutes 45 and the second WAP server 2 minutes 30. That is a total of 9 minutes and 45 seconds for a highly available ADFS and Reverse Proxy solution which is a whole lot better than configuring UAG.

Note that I am giving the option of using an internal Certificate Authority for the deployment by specifying ‘-OnlineRequest’ in the scripts. This was done to help me with testing, however in the real world you would use a public certificate on the Web Application Proxy for the publishing rules as well as ADFS. I normally use the same public ADFS certificate on the internal ADFS server to keep the configuration consistent and allow non-domain joined clients and mobile devices connecting via internal Wifi to connect without having to import an internal root certificate.

Prerequisites

  • Four Windows 2012 R2 Preview servers which are joined to the domain and use internal DNS
  • In order to publish Exchange OWA and ECP applications with ADFS authentication, the Web Application Proxy servers must be domain joined in order to perform KCD. This makes the scripts easier too as they can place and retrieve certificates from a share and request online certificates from the enterprise Certificate Authority
  • Certificate PFX files for ADFS and Web Application Proxy exported as PFX files with a private key (unless OnlineRequest is specified in which case the script will request certificates from an Enterprise online Certificate Authority)
  • The ADFS farm FQDN (adfs2.showcase.kloud.com.au in this case) DNS A record must exist in DNS before configuration. As an aside – this should be an A record not a CNAME to allow domain joined clients with the ADFS FQDN in the IE trusted sites to authenticate automatically
  • Exchange and Lync already configured
  • One or more load balancers to spread the traffic load across the Web Application Proxy ‘cluster’ and the ADFS farm servers and perform health checks to provide high availability

Scripts

First, a note about the credential prompts. I could have entered the credentials into the script as plain text and converted them to a secure string, but I think that is bad practice as you are dealing with certificates and service accounts where the passwords will not change. Leaving text files with passwords on disk in plain text is a bad thing. Another option is to input the credentials once and save them to the registry in an encrypted form but that is not really relevant here as you will typically run the script only once per server. A third option would be to use remote PowerShell and input credentials once before running the configuration on all servers.

Edit the ‘### Variables’ section of each script and run from an elevated PowerShell prompt.

Setup-ADFSserver1.ps1

Usage:

  • .\Setup-ADFSserver1.ps1
  • .\Setup-ADFSserver1.ps1 –OnlineRequest
param (
	[switch]$OnlineRequest
)

$Start = get-date

### Variables
$CertificateADFSsubject = "adfs2.showcase.kloud.com.au"
$ADFSdisplayName = "Kloud Showcase ADFS 2"
$ADFSuser = "kloud\ADFSfarm2012R2"
$CertificateLocalPath = "C:\Certificates\"
$CertificateRemotePath = "\\dc01.kloud.internal\certificates\"
$RelyingPartyTrustExchangeName = "Exchange"
$RelyingPartyTrustExchangeURI = "https://ex2010-01.kloud.internal/owa"
$RelyingPartyTrustExchangeIssuanceRule = "@RuleTemplate = `"AllowAllAuthzRule`""    # Escape quotes
$RelyingPartyTrustExchangeIssuanceRule += "`n => issue(Type = `"http://schemas.microsoft.com/authorization/claims/permit`", Value = `"true`");"    # New line and escape quotes
# Only for Online Request
$CertificateAuthority = "dc01.kloud.internal\kloud-Showcase-CA"

### Preparation
$PfxPasswordADFS = Get-Credential "PFX password" -message "Enter the password for the ADFS PFX certificate"
if ($PfxPasswordADFS -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$ADFSuserCredential = Get-Credential $ADFSuser -message "Enter the password for the ADFS service account"
if ($ADFSuserCredential -eq $null) {
	write-host "No password entered, exiting"
	exit
}

if ((Test-Path $CertificateLocalPath) -eq $false) {
	mkdir $CertificateLocalPath
}

$CertificateADFS = "$CertificateADFSsubject.pfx"
$CertificateADFSremotePath = $CertificateRemotePath + $CertificateADFS

$CertificateADFSsubjectCN = "CN=" + $CertificateADFSsubject
cd $CertificateLocalPath

function New-CertificateRequest {
	param (
		[Parameter(Mandatory=$true, HelpMessage = "Please enter the subject beginning with CN=")]
		[ValidatePattern("CN=")]
		[string]$subject,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the SAN domains as a comma separated list")]
		[array]$SANs,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the Online Certificate Authority")]
		[string]$OnlineCA,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the Online Certificate Authority")]
		[string]$CATemplate = "WebServer"
	)

	### Preparation
	$subjectDomain = $subject.split(',')[0].split('=')[1]
	if ($subjectDomain -match "\*.") {
		$subjectDomain = $subjectDomain -replace "\*", "star"
	}
	$CertificateINI = "$subjectDomain.ini"
	$CertificateREQ = "$subjectDomain.req"
	$CertificateRSP = "$subjectDomain.rsp"
	$CertificateCER = "$subjectDomain.cer"

	### INI file generation
	new-item -type file $CertificateINI -force
	add-content $CertificateINI '[Version]'
	add-content $CertificateINI 'Signature="$Windows NT$"'
	add-content $CertificateINI ''
	add-content $CertificateINI '[NewRequest]'
	$temp = 'Subject="' + $subject + '"'
	add-content $CertificateINI $temp
	add-content $CertificateINI 'Exportable=TRUE'
	add-content $CertificateINI 'KeyLength=2048'
	add-content $CertificateINI 'KeySpec=1'
	add-content $CertificateINI 'KeyUsage=0xA0'
	add-content $CertificateINI 'MachineKeySet=True'
	add-content $CertificateINI 'ProviderName="Microsoft RSA SChannel Cryptographic Provider"'
	add-content $CertificateINI 'ProviderType=12'
	add-content $CertificateINI 'SMIME=FALSE'
	add-content $CertificateINI 'RequestType=PKCS10'
	add-content $CertificateINI '[Strings]'
	add-content $CertificateINI 'szOID_ENHANCED_KEY_USAGE = "2.5.29.37"'
	add-content $CertificateINI 'szOID_PKIX_KP_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"'
	add-content $CertificateINI 'szOID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"'
	if ($SANs) {
		add-content $CertificateINI 'szOID_SUBJECT_ALT_NAME2 = "2.5.29.17"'
		add-content $CertificateINI '[Extensions]'
		add-content $CertificateINI '2.5.29.17 = "{text}"'

		foreach ($SAN in $SANs) {
			$temp = '_continue_ = "dns=' + $SAN + '&"'
			add-content $CertificateINI $temp
		}
	}

	### Certificate request generation
	if (test-path $CertificateREQ) {del $CertificateREQ}
	certreq -new $CertificateINI $CertificateREQ

	### Online certificate request and import
	if ($OnlineCA) {
		if (test-path $CertificateCER) {del $CertificateCER}
		if (test-path $CertificateRSP) {del $CertificateRSP}
		certreq -submit -attrib "CertificateTemplate:$CATemplate" -config $OnlineCA $CertificateREQ $CertificateCER

		certreq -accept $CertificateCER
	}
}

### Script

if ($OnlineRequest) {

    # Certificate creation
    New-CertificateRequest -subject $CertificateADFSsubjectCN -OnlineCA $CertificateAuthority

    # Certificate export
    $ADFScertificate = dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateADFSsubject}
    if (($ADFScertificate.count) -gt 1) {
	    write-host -foregroundcolor Yellow "`n`nDuplicate certifcates! Delete all certificates with the subject $CertificateADFSsubject"
	    exit
    }
    Export-PfxCertificate -Cert $ADFScertificate -FilePath $CertificateADFSremotePath -Password $PfxPasswordADFS.Password
}
else {
    Import-PfxCertificate –FilePath $CertificateADFSremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordADFS.Password
}

# ADFS Install
Add-WindowsFeature ADFS-Federation -IncludeManagementTools
Import-Module ADFS
$CertificateThumbprint = (dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateADFSsubjectCN}).thumbprint
Install-AdfsFarm -CertificateThumbprint $CertificateThumbprint -FederationServiceDisplayName $ADFSdisplayName -FederationServiceName $CertificateADFSsubject -ServiceAccountCredential $ADFSuserCredential

# ADFS Non Claims Aware Relying Party Trust
Add-AdfsNonClaimsAwareRelyingPartyTrust -Name $RelyingPartyTrustExchangeName -Identifier $RelyingPartyTrustExchangeURI -IssuanceAuthorizationRules $RelyingPartyTrustExchangeIssuanceRule

$Finish = get-date
$Elapsed = $finish - $start
"Elapsed time: {0:mm} minutes and {0:ss} seconds" -f $Elapsed

Setup-ADFSserver2.ps1

Usage: .\Setup-ADFSserver2.ps1

$Start = get-date

### Variables
$CertificateADFSsubject = "adfs2.showcase.kloud.com.au"
$ADFSprimaryServer = "2012R2ADFS1.kloud.internal"
$ADFSuser = "kloud\ADFSfarm2012R2"
$CertificateLocalPath = "C:\Certificates\"
$CertificateRemotePath = "\\dc01.kloud.internal\certificates\"

### Preparation
$PfxPasswordADFS = Get-Credential "PFX password" -message "Enter the password for the PFX certificate"
if ($PfxPasswordADFS -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$ADFSuserCredential = Get-Credential $ADFSuser -message "Enter the password for the ADFS service account"
if ($ADFSuserCredential -eq $null) {
	write-host "No password entered, exiting"
	exit
}

$CertificateADFS = "$CertificateADFSsubject.pfx"
$CertificateADFSremotePath = $CertificateRemotePath + $CertificateADFS

### Script
# Import ADFS Certificate
if ((Test-Path $CertificateLocalPath) -eq $false) {
	mkdir $CertificateLocalPath
}
cd $CertificateLocalPath
Import-PfxCertificate –FilePath $CertificateADFSremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordADFS.Password
$CertificateThumbprint = (dir Cert:\LocalMachine\My | where {$_.subject -match $ADFSfqdn}).thumbprint

# ADFS Install
Add-WindowsFeature ADFS-Federation -IncludeManagementTools
Import-Module ADFS
Add-AdfsFarmNode -CertificateThumbprint $CertificateThumbprint -ServiceAccountCredential $ADFSuserCredential -PrimaryComputerName $ADFSprimaryServer -PrimaryComputerPort 80

$Finish = get-date
$Elapsed = $finish - $start
"Elapsed time: {0:mm} minutes and {0:ss} seconds" -f $Elapsed

Setup-WAPserver1.ps1

Usage:

  • .\Setup-WAPserver1.ps1
  • .\Setup-WAPserver1.ps1 –OnlineRequest
param (
	[switch]$OnlineRequest
)

$Start = get-date

### Variables
# WAP and ADFS
$CertificateWAPsubject = "mail.showcase.kloud.com.au"
$CertificateADFSsubject = "adfs2.showcase.kloud.com.au"
$CertificateWAPsans = "mail.showcase.kloud.com.au","autodiscover.showcase.kloud.com.au","lyncws.showcase.kloud.com.au","lyncdiscover.showcase.kloud.com.au","meet.showcase.kloud.com.au","dialin.showcase.kloud.com.au","owas.showcase.kloud.com.au","teams.showcase.kloud.com.au","my.showcase.kloud.com.au"
$ADFSadministrator = "kloud\administrator"
$CertificateRemotePath = "\\dc01.kloud.internal\certificates\"
$CertificateLocalPath = "C:\Certificates\"
# Lync
$LyncDomain = "showcase.kloud.com.au"
$LyncWebServicesRoot = "lyncws."
$OfficeWebAppsRoot = "owas."
# Exchange
$AdfsExchangeRelyingParty = "Exchange"
$ExchangeExternalURL = "https://mail.showcase.kloud.com.au/"
$ExchangeAutodiscoverURL = "https://autodiscover.showcase.kloud.com.au/"
$ExchangeInternalURL = "https://ex2010-01.kloud.internal/"
$ExchangeSPN = "http/ex2010-01.kloud.internal"
#SNI
$SniIPport = "0.0.0.0:443" # IP and port to bind to. 0.0.0.0:443 matches all
# Only for Online Request
$CertificateAuthority = "dc01.kloud.internal\kloud-Showcase-CA"

### Preparation
$PfxPasswordADFS = Get-Credential "ADFS PFX password" -message "Enter the password for the PFX certificate"
if ($PfxPasswordADFS -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$PfxPasswordWAP = Get-Credential "WAP PFX password" -message "Enter the password for the PFX certificate"
if ($PfxPasswordWAP -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$ADFScredentials = Get-Credential $ADFSadministrator -message "Enter the password for the ADFS service account"
if ($ADFScredentials -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$CertificateADFS = "$CertificateADFSsubject.pfx"
$CertificateWAP = "$CertificateWAPsubject.pfx"
$CertificateADFSremotePath = $CertificateRemotePath + $CertificateADFS
$CertificateWAPremotePath = $CertificateRemotePath + $CertificateWAP
$CertificateWAPsubjectCN = "CN=" + $CertificateWAPsubject

function New-CertificateRequest {
	param (
		[Parameter(Mandatory=$true, HelpMessage = "Please enter the subject beginning with CN=")]
		[ValidatePattern("CN=")]
		[string]$subject,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the SAN domains as a comma separated list")]
		[array]$SANs,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the Online Certificate Authority")]
		[string]$OnlineCA,
		[Parameter(Mandatory=$false, HelpMessage = "Please enter the Online Certificate Authority")]
		[string]$CATemplate = "WebServer"
	)

	### Preparation
	$subjectDomain = $subject.split(',')[0].split('=')[1]
	$CertificateINI = "$subjectDomain.ini"
	$CertificateREQ = "$subjectDomain.req"
	$CertificateRSP = "$subjectDomain.rsp"
	$CertificateCER = "$subjectDomain.cer"

	### INI file generation
	new-item -type file $CertificateINI -force
	add-content $CertificateINI '[Version]'
	add-content $CertificateINI 'Signature="$Windows NT$"'
	add-content $CertificateINI ''
	add-content $CertificateINI '[NewRequest]'
	$temp = 'Subject="' + $subject + '"'
	add-content $CertificateINI $temp
	add-content $CertificateINI 'Exportable=TRUE'
	add-content $CertificateINI 'KeyLength=2048'
	add-content $CertificateINI 'KeySpec=1'
	add-content $CertificateINI 'KeyUsage=0xA0'
	add-content $CertificateINI 'MachineKeySet=True'
	add-content $CertificateINI 'ProviderName="Microsoft RSA SChannel Cryptographic Provider"'
	add-content $CertificateINI 'ProviderType=12'
	add-content $CertificateINI 'SMIME=FALSE'
	add-content $CertificateINI 'RequestType=PKCS10'
	add-content $CertificateINI '[Strings]'
	add-content $CertificateINI 'szOID_ENHANCED_KEY_USAGE = "2.5.29.37"'
	add-content $CertificateINI 'szOID_PKIX_KP_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"'
	add-content $CertificateINI 'szOID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"'
	if ($SANs) {
		add-content $CertificateINI 'szOID_SUBJECT_ALT_NAME2 = "2.5.29.17"'
		add-content $CertificateINI '[Extensions]'
		add-content $CertificateINI '2.5.29.17 = "{text}"'

		foreach ($SAN in $SANs) {
			$temp = '_continue_ = "dns=' + $SAN + '&"'
			add-content $CertificateINI $temp
		}
	}

	### Certificate request generation
	if (test-path $CertificateREQ) {del $CertificateREQ}
	certreq -new $CertificateINI $CertificateREQ

	### Online certificate request and import
	if ($OnlineCA) {
		if (test-path $CertificateCER) {del $CertificateCER}
		if (test-path $CertificateRSP) {del $CertificateRSP}
		certreq -submit -attrib "CertificateTemplate:$CATemplate" -config $OnlineCA $CertificateREQ $CertificateCER

		certreq -accept $CertificateCER
	}
}

### Script

# Import ADFS Certificate
if ((Test-Path $CertificateLocalPath) -eq $false) {
	mkdir $CertificateLocalPath
}
cd $CertificateLocalPath
Import-PfxCertificate –FilePath $CertificateADFSremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordADFS.Password

if ($OnlineRequest) {
	# Request Web Application Proxy certificate
	New-CertificateRequest -subject $CertificateWAPsubjectCN -SANs $CertificateWAPsans -OnlineCA $CertificateAuthority

	# Export Web Application Proxy certificate
	$WAPcertificate = dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateWAPsubject}
	if (($WAPcertificate.count) -gt 1) {
		write-host -foregroundcolor Yellow "`n`nDuplicate certifcates! Delete all certificates with the subject $CertificateWAPsubject"
		exit
	}
	Export-PfxCertificate -Cert $WAPcertificate -FilePath $CertificateWAPremotePath -Password $PfxPasswordWAP.Password
}
else {
Import-PfxCertificate –FilePath $CertificateWAPremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordWAP.Password
}

# Add Web Application Proxy Role
Install-WindowsFeature Telnet-Client, RSAT-AD-PowerShell, Web-Application-Proxy -IncludeManagementTools

# Web Application Proxy Configuration Wizard
$CertificateADFSThumbprint = (dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateADFSsubject}).thumbprint
Install-WebApplicationProxy -CertificateThumbprint $CertificateADFSThumbprint -FederationServiceName $CertificateADFSsubject -FederationServiceTrustCredential $ADFScredentials

## Web Application Proxy Applications
$CertificateWAPThumbprint = (dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateWAPsubject}).thumbprint

# Lync
Add-WebApplicationProxyApplication -Name 'Lync Web Services' -ExternalPreAuthentication PassThrough -ExternalUrl "https://$LyncWebServicesRoot$LyncDomain/" -BackendServerUrl ("https://"+$LyncWebServicesRoot+$Lyncdomain+":4443/") -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Lync Lyncdiscover' -ExternalPreAuthentication PassThrough -ExternalUrl "https://lyncdiscover.$LyncDomain/" -BackendServerUrl ("https://lyncdiscover."+$LyncDomain+":4443/") -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Lync Dialin' -ExternalPreAuthentication PassThrough -ExternalUrl "https://dialin.$LyncDomain/" -BackendServerUrl ("https://dialin."+$LyncDomain+":4443/") -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Lync Meet' -ExternalPreAuthentication PassThrough -ExternalUrl "https://meet.$LyncDomain/" -BackendServerUrl ("https://meet."+$LyncDomain+":4443/") -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Office Web Apps Server' -ExternalPreAuthentication PassThrough -ExternalUrl "https://$OfficeWebAppsRoot$LyncDomain/" -BackendServerUrl ("https://"+$OfficeWebAppsRoot+$LyncDomain+"/") -ExternalCertificateThumbprint $CertificateWAPThumbprint

# Exchange

Add-WebApplicationProxyApplication -Name 'Exchange OWA' -ExternalPreAuthentication ADFS -ADFSRelyingPartyName $AdfsExchangeRelyingParty -ExternalUrl $ExchangeExternalURL"owa/" -BackendServerUrl $ExchangeInternalURL"owa/" -BackendServerAuthenticationSpn $ExchangeSPN -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange ECP' -ExternalPreAuthentication ADFS -ADFSRelyingPartyName $AdfsExchangeRelyingParty -ExternalUrl $ExchangeExternalURL"ecp/" -BackendServerUrl $ExchangeInternalURL"ecp/" -BackendServerAuthenticationSpn $ExchangeSPN -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange Autodiscover' -ExternalPreAuthentication PassThrough -ExternalUrl $ExchangeAutodiscoverURL"autodiscover/" -BackendServerUrl $ExchangeInternalURL"autodiscover/" -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange Outlook Anywhere' -ExternalPreAuthentication PassThrough -ExternalUrl $ExchangeExternalURL"rpc/" -BackendServerUrl $ExchangeInternalURL"rpc/" -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange OAB' -ExternalPreAuthentication PassThrough -ExternalUrl $ExchangeExternalURL"OAB/" -BackendServerUrl $ExchangeInternalURL"OAB/" -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange EWS' -ExternalPreAuthentication PassThrough -ExternalUrl $ExchangeExternalURL"EWS/" -BackendServerUrl $ExchangeInternalURL"EWS/" -ExternalCertificateThumbprint $CertificateWAPThumbprint
Add-WebApplicationProxyApplication -Name 'Exchange ActiveSync' -ExternalPreAuthentication PassThrough -ExternalUrl $ExchangeExternalURL"Microsoft-Server-ActiveSync/" -BackendServerUrl $ExchangeInternalURL"Microsoft-Server-ActiveSync/" -ExternalCertificateThumbprint $CertificateWAPThumbprint

## Exchange SPN for KCD

Import-Module -Name ActiveDirectory
$WAPserver = Get-ADComputer (hostname)
Set-ADObject $WAPserver.DistinguishedName -Add @{"msDS-AllowedToDelegateTo" = $ExchangeSPN}
Set-ADObject $WAPserver.DistinguishedName -Replace @{"userAccountControl" = 16781312}

# Set default SNI binding
sleep 15 # sleep for 15 seconds to allow WAP bindings to be set
$certsList = @()
$certs = netsh http show sslcert | where {$_ -match ":port" -or $_ -match "Certificate Hash" -or $_ -match "Application ID"}
$certs = $certs.replace(" : ","#").replace(" ","")
$certs | foreach {
	if ($_ -match ":port") {
		$tempObject = New-Object -Type PSObject
		$tempObject | Add-Member -Type noteproperty -Name Binding -value $_.split("#")[1]
	}
	elseif ($_ -match "CertificateHash") {
		$tempObject | Add-Member -Type noteproperty -Name CertificateHash -value ($_.split("#")[1]-replace " ")
	}
	else {
		$tempObject | Add-Member -Type noteproperty -Name ApplicationID -value ($_.split("#")[1]-replace " ")
		$certsList += $tempObject
	}
}

$match = $certsList | where {$_.Binding -match $CertificateWAPsubject}
$CertificateHash = $match.CertificateHash
$ApplicationID = $match.ApplicationID

write-host -ForegroundColor Green "`n`nDefault bindings before changes"
netsh http show sslcert | where {$_ -match "ip:port"}

$command = "http add sslcert ipport=$SniIPport certhash=$CertificateHash appid=$ApplicationID"
write-host -ForegroundColor Yellow "`n`nAdding Certificate"
$command | netsh

write-host -ForegroundColor Yellow "`n`nDefault bindings after changes. Check that $SniIPport exists"
netsh http show sslcert | where {$_ -match "ip:port"}

$Finish = get-date
$Elapsed = $finish - $start
"Elapsed time: {0:mm} minutes and {0:ss} seconds" -f $Elapsed

Setup-WAPserver2.ps1

Usage: .\Setup-WAPserver2.ps1

$Start = get-date

### Variables
$CertificateWAPsubject = "mail.showcase.kloud.com.au"
$CertificateADFSsubject = "adfs2.showcase.kloud.com.au"
$ADFSadministrator = "kloud\administrator"
$CertificateRemotePath = "\\dc01.kloud.internal\certificates\"
$CertificateLocalPath = "C:\Certificates\"
$ExchangeSPN = "http/ex2010-01.kloud.internal"
$SniIPport = "0.0.0.0:443" # IP and port to bind to. 0.0.0.0:443 matches all

### Preparation
$PfxPasswordADFS = Get-Credential "ADFS PFX password" -message "Enter the password for the PFX certificate"
if ($PfxPasswordADFS -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$PfxPasswordWAP = Get-Credential "WAP PFX password" -message "Enter the password for the PFX certificate"
if ($PfxPasswordWAP -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$ADFScredentials = Get-Credential $ADFSadministrator -message "Enter the password for the ADFS service account"
if ($ADFScredentials -eq $null) {
	write-host "No password entered, exiting"
	exit
}
$CertificateADFS = "$CertificateADFSsubject.pfx"
$CertificateWAP = "$CertificateWAPsubject.pfx"
$CertificateADFSremotePath = $CertificateRemotePath + $CertificateADFS
$CertificateWAPremotePath = $CertificateRemotePath + $CertificateWAP
$CertificateWAPsubjectCN = "CN=" + $CertificateWAPsubject

### Script

# Import ADFS Certificate
if ((Test-Path $CertificateLocalPath) -eq $false) {
	mkdir $CertificateLocalPath
}
cd $CertificateLocalPath
Import-PfxCertificate –FilePath $CertificateADFSremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordADFS.Password

# Import WAP Certificate
Import-PfxCertificate –FilePath $CertificateWAPremotePath -CertStoreLocation cert:\localMachine\my -Password $PfxPasswordWAP.Password

# Add Web Application Proxy Role
Install-WindowsFeature Telnet-Client, RSAT-AD-PowerShell, Web-Application-Proxy -IncludeManagementTools

# Web Application Proxy Configuration Wizard
$CertificateADFSThumbprint = (dir Cert:\LocalMachine\My | where {$_.subject -match $CertificateADFSsubject}).thumbprint
Install-WebApplicationProxy -CertificateThumbprint $CertificateADFSThumbprint -FederationServiceName $CertificateADFSsubject -FederationServiceTrustCredential $ADFScredentials

## Exchange SPN for KCD
Import-Module -Name ActiveDirectory
$WAPserver = Get-ADComputer (hostname)
Set-ADObject $WAPserver.DistinguishedName -Add @{"msDS-AllowedToDelegateTo" = $ExchangeSPN}
Set-ADObject $WAPserver.DistinguishedName -Replace @{"userAccountControl" = 16781312}

# Set default SNI binding
sleep 15 # sleep for 15 seconds to allow WAP bindings to be set
$certsList = @()
$certs = netsh http show sslcert | where {$_ -match ":port" -or $_ -match "Certificate Hash" -or $_ -match "Application ID"}
$certs = $certs.replace(" : ","#").replace(" ","")
$certs | foreach {
	if ($_ -match ":port") {
		$tempObject = New-Object -Type PSObject
		$tempObject | Add-Member -Type noteproperty -Name Binding -value $_.split("#")[1]
	}
	elseif ($_ -match "CertificateHash") {
		$tempObject | Add-Member -Type noteproperty -Name CertificateHash -value ($_.split("#")[1]-replace " ")
	}
	else {
		$tempObject | Add-Member -Type noteproperty -Name ApplicationID -value ($_.split("#")[1]-replace " ")
		$certsList += $tempObject
	}
}

$match = $certsList | where {$_.Binding -match $CertificateWAPsubject}
$CertificateHash = $match.CertificateHash
$ApplicationID = $match.ApplicationID

write-host -ForegroundColor Green "`n`nDefault bindings before changes"
netsh http show sslcert | where {$_ -match "ip:port"}

$command = "http add sslcert ipport=$SniIPport certhash=$CertificateHash appid=$ApplicationID"
write-host -ForegroundColor Yellow "`n`nAdding Certificate"
$command | netsh

write-host -ForegroundColor Yellow "`n`nDefault bindings after changes. Check that $SniIPport exists"
netsh http show sslcert | where {$_ -match "ip:port"}

$Finish = get-date
$Elapsed = $finish - $start
"Elapsed time: {0:mm} minutes and {0:ss} seconds" -f $Elapsed

Add the servers to the load balancer, external DNS entries and you should be able to login to OWA and join a Lync meeting.

Autodiscover Troubleshooting

Notes from the Field

I have been onsite working on remediating a partially completed Exchange 2007 to Exchange 2010 migration. This environment was then configured for Exchange Online Hybrid using ADFS 2.0 and Dirsync.

After reviewing the Autodiscover configuration, I discovered that something wasn’t right. In addition to this, I had received the following issues list from the customer.

Symptoms

  1. Outlook for Office 365 mailboxes is not able to be configured using Autodiscover. This occurred on both domain and non-domain joined machines.
  2. Outlook on domain joined machines is intermittently unable to be configured using Autodiscover
  3. Outlook on domain joined machines is intermittently very slow

These symptoms combined told me that there was likely more than one issue in effect here due to the fact that both domain joined and non-domain joined machines were affected.

Topology

The network architecture had multiple Active Directory sites across all regions with Client Access Servers located in all regions but not all sites.

Customer_Autodiscover_Generic

Resolution

1. Office 365 mailboxes unable to be configured using autodiscover

Upon investigating where the internal DNS record for autodiscover.domain.com pointed to, I discovered that this record was being pointed to one of the Exchange 2007 Client Access Servers. As part of the Hybrid Configuration for Office 365, Autodiscover records are required to be updated to point to the Exchange 2010 hybrid servers.

After updating the internal DNS record, Autodiscover began working correctly for non-domain joined machines. For domain joined machines, this process began working intermittently however another piece of the puzzle was required in order to fix this issue.

2. Outlook on domain joined machines is intermittently unable to be configured using Autodiscover

and

3. Outlook on domain joined machines is intermittently very slow

Knowing that Active Directory SCP records will be in effect here, I began looking into the configuration of two attributes assigned to the various SCP records.

  • serviceBindingInformation – This attribute returns what is effectively the Autodiscover url to the domain joined machine
  • keywords – This attribute returns a variety of values depending on configuration, but in this case, the value we are interested in is Site=

In this case, all SCP records, except for the 2 Exchange 2007 Client Access Servers were configured with a Site= keyword.

This value however only contained the site name for the location where the Client Access Server was installed.

When SCP lookup is conducted, AD is queried for the site name of the Client. The lookup then tries to match this with a site= value in the keyword attribute of the SCP record.

Considering this, a site name will nearly never be matched, as 90% of the Client Access Servers are located in datacentres in this environment.

The fall back behaviour of SCP when no match is found, is that where the SCP URL record contains at least one keyword that starts with Site= but none of these keywords read Site=siteName.

So what does this all mean? This means that in effect, any SCP record with a value of Site=Anyvalue could be returned to the client. My testing showed that this is in fact what was occurring. The first SCP record in the list by alphabetical order was being returned to the client. It just turned out that this server was located in a datacentre in the UK.

Armed with all of this information, I recalled a set-clientaccessserver setting called autodiscoversitescope. This setting allows us to bind Active Directory site names to specific client access servers. In effect, what this does on the back end is populate the keywords attribute with site= values for all sites that are defined in the autodiscoversitescope.

Once all of the Active Directory Sites were assigned to the autodiscoversitescope attribute on the appropriate regional Client Access Servers, all clients were now redirected to the local servers for connection.

This configuration resolved both of the identified issues above as SCP lookups were now being completed locally on the Client Access Servers.

Windows 2012 R2 Preview Web Application Proxy – Exchange 2013 Publishing Tests

==================
Updated: 10 September 2013

==================
Updated: 15 July 2013

  • I have heard from a member of the Web Application Proxy product group who said there is a bug in the Preview version that prevents Outlook Anywhere from working. They say it will be fixed in the RTM version
  • Lync 2013 and  Office Web Apps 2013 have been tested and work with some configuration changes. See https://blog.kloud.com.au/2013/07/15/publish-lync-2013-with-2012-r2-preview-web-application-proxy/ 
  • ActiveSync does not support SNI so a default binding needs to be set on the Web Application Proxy as per the post above to make it work
  • Clarification about ADFS being a hard requirement for the Web Application Proxy, even if only doing pass-through
  • Clarification about modifying published applications
  • Clarification about case sensitivity

==================

The announcement from Microsoft TechEd North America early in June that got me really interested was the Windows 2012 R2 Web Application Proxy which was described as:

Web Application Proxy – The Web Application Proxy is a new role service in the Windows Server Remote Access role. It provides the ability to publish access to corporate resources, and enforce multi-factor authentication as well as apply conditional access policies to verify both the user’s identity and the device they are using resources, and enforce multi-factor authentication as well as verify the device being used before access is granted.

The Web Application Proxy is a reverse proxy and ADFS (Active Directory Federation Services) Proxy that also provides functionality like Workplace Join for Windows 8.1 using the Device Registration Service (DRS). I have a particular interest in the reverse proxy side having done a lot of work with UAG lately which makes me miss TMG! As discussed in my previous blog Publish Lync 2013 Including Mobility and Office Web Apps with UAG 2010, with TMG disappearing the only Microsoft product (other than IIS with ARR) with reverse proxy functionality is Microsoft Forefront UAG (Unified Access Gateway).

UAG development seemed to have slowed down and the latest Service Pack added support for Exchange 2013 and SharePoint 2013 but not even Lync 2013 that came out at the same time. No word has been heard about any future development and with people like Erez Ben Ari (Ben Ari on TechNet) moving to the IIS team it seems like the writing is on the wall for Microsoft’s Forefront TMG and UAG products. UAG is a very powerful product but is hamstrung by its legacy roots in e-Gap and IAG and reliance on TMG underneath. TMG is not supported on Windows 2012 so if it was to move forward a total rewrite would have been needed.

In this blog I cover a bit about the Web Application Proxy and show how to configure ADFS, the Web Application Proxy and publish a claims-aware test application. I also did some testing with publishing Exchange 2013 to see how it worked.

TechEd Sessions and Documentation about Web Application Proxy

A session at TechEd North America titled Enable Work from Anywhere without Losing Sleep: Remote Access with the Web Application Proxy and VPN Solutions showed off some of the functionality (video and slides available at the link) and the same session at TechEd Europe last week WCA-B333 came out with a lot more information that I only saw after I started testing unfortunately. There is now a single TechNet page dedicated to the Web Application Proxy http://technet.microsoft.com/en-us/library/dn280944.aspx

Web Application Proxy Functionality

The Web Application Proxy (WAP) is a Role Service under the Remote Access role of Windows 2012 which also includes DirectAccess, VPN and routing services. It can provide simple reverse proxy functionality using ‘Pass-through’ where no preauthentication is performed, or provide Active Directory Federation Services (AD FS or ADFS) authentication by performing the ADFS proxy function. Note that even in Pass-through mode, WAP needs a Windows Server 2012 R2 Preview ADFS farm and must be setup as an ADFS Proxy. Without ADFS you can’t even complete the configuration wizard. Pass-through and ADFS federation to claims aware applications can be performed like previous AD FS proxies as a workgroup machine in the DMZ.

The reverse proxy functionality that seems like it could be a TMG/UAG replacement is the ability for the WAP to provide preauthentication for non-claims aware backend applications. It takes ADFS authentication and initiates a new session to the backend server providing Single Sign On (SSO) across multiple backend applications. This was possible with UAG by modifying applications to use the Claims to Windows Token Service (C2WTS) as described in Access OWA with ADFS, but the promise of this new functionality is that no modification is required on the backend service.

The catch in this scenario is that the WAP can only provide preauthentication and backend authentication to non-claims aware applications published with Integrated Windows Authentication (IWA) using Kerberos. No IWA with NTLM or basic authentication support. NTLM and basic are supported in Pass-through mode only. So this is not a full UAG/TMG replacement for applications such as Exchange that typically would have preauthentication performed by the reverse proxy. Slide 9 from the Europe WCA-B333 session mentioned above, shows the preauthentication support.

In my testing though, it wasn’t as simple as this as NTLM and Basic did not work for rich clients – even in pass-through mode (Update: for ActiveSync this is fixed by adding a default SSL binding as ActiveSync does not support SNI) . Slide 40 on the same presentation states that UAG will still be supported until 2015. Additionally it says that the Web Application Proxy is ‘part of a broader BYO Access platform alongside AD, AD FS, Intune’ and ‘We are not building all of UAG functionality into Windows Server’, so it looks like Web Application Proxy may not be a total replacement for UAG.

Exchange 2013 Testing

There is very little documentation right now for the Web Application Proxy so the following are my observations and assumptions based on testing Windows Server 2012 R2 Preview in Windows Azure. That meant I could only have a single network card (a restriction of Azure) and I don’t know if that would change any functionality (as it would for TMG and UAG).

ADFS and KCD Requirements

The first important thing to note is that any preauthentication or ADFS proxy functionality requires Windows Server 2012 R2 ADFS on the backend. So for anything other than Pass-through, ADFS is a hard dependency. It was mentioned that the recommendation now is to run ADFS on a domain controller and ADFS seems to be taking over as the primary authentication mechanism for all Windows applications. Additionally a schema upgrade will be required to support all of the device registration functionality.

The second important thing to know is that in order to do preauthentication to backend applications, the WAP must be domain joined as Kerberos Constrained Delegation (KCD) is required. In the past ADFS proxies were not domain joined and the WAP can act the same way as long as KCD is not needed.

Setup ADFS

Setup a Domain Controller and add the ADFS role. Add a ‘Non-Claims-Aware Relying Party Trust’ for the Exchange CAS or CAS Array as can be seen below.

For the Issuance Authorization Rules, either ‘Permit All Users’ or you can limit who can authenticate through ADFS (and the Web Application Proxy) using Active Directory attributes. In this example I have created an AD group that allows access through the WAP. In the Issuance Authorization Rules, specify ‘Permit or Deny Users Based on an Incoming Claim’, specify ‘Group SID’ and select the relevant group. This allow the full flexibility of ADFS claims rules which can include specifying IP subnets, whether people are coming through the ADFS Proxy and many more.

Setup Web Application Proxy

Join the computer to the domain and install the Remote Access Role, and the Web Application Proxy Role Service.

Import the ADFS public certificate and any other certificates required for publishing. As with Server 2012, 2012 R2 supports Server Name Indication (SNI) which was covered previously on the Kloud blog An Overview of Server Name Indication (SNI) and Creating an IIS SNI Web SSL Binding Using PowerShell in Windows Server 2012. This allows multiple certificates to be used on a single IP and port combination, which is a great improvement over TMG and UAG.

Start the Web Application Proxy configuration wizard and setup the server as an ADFS proxy.

Publish an ‘Active Directory Federation Services (AD FS)’ application

Specify the Relying Party from the list of enabled Relying Parties configured on the on-premise domain joined ADFS farm server.

Setup the external and internal URLs and SPN for the internal web server. The publishing can either be a specific path or the whole server, however unlike TMG there is no ordering of applications so if you publish a whole server you cannot add a sub path as a higher rule. In this case I have published Exchange OWA and ECP as separate applications.

Note the warning at the top about the external and internal URLs not matching. That help topic is not live so I do not know what problems can arise, but this is probably for applications like SharePoint 2013 with Host Name Site Collections that require the external and internal URLs to match. And this is where you see the benefit of a modern reverse proxy – it shows you the PowerShell commands to create the application. Also make sure to add the trailing ‘/’ on both the external and internal URLs or it will complain.

For the ECP application I can create it with PowerShell:

Add-WebApplicationProxyApplication -BackendServerAuthenticationSpn 'http/marc-exchange1.marc.kloud.com.au' -BackendServerUrl 'https://marc-exchange1.marc.kloud.com.au/ecp/' -ExternalCertificateThumbprint 'F7F6B300FE4D569FD598E1C9722571CF9AD780DD' -ExternalUrl 'https://mail.marc.kloud.com.au/ecp/' -Name 'Exchange ECP' -ExternalPreAuthentication ADFS -ADFSRelyingPartyName 'Exchange'

The final task is to setup KCD to allow the WAP to impersonate the user and obtain a Kerberos ticket to access the Exchange CAS. This is done by editing the WAP computer object and allowing delegation to the Exchange CAS SPN, or CAS Array Alternate Service Account user or computer object. A PowerShell function for this is provided in my previous blog post Outlook Anywhere NTLM SSO with UAG 2010 KCD

Testing Web Clients

Browse to the OWA URL e.g. https://mail.marc.kloud.com.au/owa/ which redirects to

https://adfs.marc.kloud.com.au/adfs/ls?version=1.0&action=signin&realm=urn:AppProxy:com&appRealm=d2b07cee-c3e0-e211-af1e-00155d53e448&returnUrl=https://mail.marc.kloud.com.au/owa/?bO=1

Logging in with a domain user who is not a member of the ‘WAP Allowed’ group gives a friendly error:

While a user who is a member of the group gets OWA

Testing Rich Clients

In order to test Outlook Anywhere and ActiveSync rich clients, instead of creating separate applications for RPC, OAB, Autodiscover and Microsoft-Server-ActiveSync applications I published the whole server https://mail.marc.kloud.com.au/ as a test. This meant that Outlook couldn’t even get autodiscover as it doesn’t understand ADFS authentication. So back to separate applications and added autodiscover and RPC for Outlook Anywhere as Pass-through applications

This allowed a new Outlook profile autodiscover to work but Outlook just hangs on the ‘Logging on to the mail server’ for a few minutes and fails to connect.

I tried to change the Exchange internal and external URLs to be same to make sure the lack of body rewriting in WAP wasn’t causing the problem. I published the root of the server https://mail.marc.kloud.com.au/ and autodiscover https://autodiscover.marc.kloud.com.au/ in Pass-through mode but still could not get Outlook Anywhere or ActiveSync to work initially (Update: ActiveSync does not support SNI, so in order to make it work a default SSL binding must be added as mentioned in my post about Lync Publish Lync 2013 with 2012 R2 Preview Web Application Proxy). Possibly this is something in my configuration as according to the slide above, NTLM and Basic authentication should just work in Pass-through mode. If somebody else gets Outlook Anywhere to work, please let me know. (Update: according to a member of the Web Application Proxy product group there is a bug in the Preview version that prevents Outlook Anywhere from working. They say it will be fixed in the RTM version).

Testing Claims Aware Applications

Just to round out the testing, I used the WIF SDK 3.5 sample claims app ‘PassiveRedirectBasedClaimsAwareWebApp’ and configured it on a Windows 2012 R2 server using the process described in the ADFS and SSO lab setup guide. Create the Relying Party Trust as described in the documentation.

On the Web Application Proxy, add an ADFS preauthenticated application based on the ‘Claims App’ RP and configure the external and internal URLs

The end result is the published application:

WAP applications

Logging on from an external client shows the claims that the client has in the ADFS security token.

Opening other ADFS preauthenticated applications provides SSO as the client has a valid ADFS cookie and a valid Web Application Proxy cookie.

My Thoughts

This is a preview release that is probably 6 months out from final release, so many things may change before RTM. With very little documentation I am guessing my way through some of the scenarios and configuration, so don’t take this a final analysis of the product.

The Good

  • Single server for ADFS Proxy, Reverse Proxy, DirectAccess and VPN
  • Totally PowerShell enabled and integrated into Windows
  • Server Name Indication (SNI) support to provide a single ‘listener’ with multiple certificates
  • Not based on IIS
  • Stateless server with centralised ADFS configuration
  • Provides new solutions like Workplace Join to enable work from anywhere and long term persistent SSO for authenticated and authorised devices
  • Multi Factor Authentication (MFA) looks easy to define, but I couldn’t test it now

The Different

  • Windows 2012 R2 Preview ADFS farm is required to install Web Application Proxy
  • Cannot be an ADFS proxy for a previous ADFS version
  • Non-SNI compatible clients like Lync mobile and Exchange ActiveSync require additional configuration on the Web Application Proxy to support them
  • Case sensitive and requires trailing / for path based application publishing. https://mail.marc.kloud.com.au/owa/ does not equal https://mail.marc.kloud.com.au/OWA/ This isn’t an issue if you publish the root of the server. This is consistent with the web standards and Unix/Linux web servers that are case sensitive. It is a departure from IIS and TMG which do not care about case

The Bad aka ‘Hopefully will be added or fixed’

  • No Basic or NTLM preauthentication
  • Hard requirement on ADFS, even if only doing Pass-through
  • Pass-through not allowing Outlook Anywhere to work – may just be me!
  • No SSO for non-ADFS published applications. Pass-through applications each do their own authentication
  • No ability to edit published application on the WAP server in the GUI. This can be done from PowerShell only. See http://technet.microsoft.com/en-us/library/dn283404
  • No external HTTP to HTTPS redirect, or HTTP publishing
  • Descoped the full URL translation like IIS ARR can do in this release. Header translation only, not body translation
  • Exchange Outlook Anywhere is not supported with preauthentication. Planned to support in a future version
  • No built in outbound load balancing to the backend servers so it requires a load balancer. TMG and UAG can provide this currently even if it isn’t used much. This support would remove a barrier to entry for some smaller companies

I would also like to see the ability in the console and WAP PowerShell cmdlets to set a default SSL binding certificate for non-SNI supported browsers so it doesn’t have to be done via netsh.