Category Archives: Exchange Online

Office 365, ADFS and double logons: WAP to the rescue!

One of the things that are causing a lot of headache using ADFS with ADFS-proxy in combination with Office 365, is the fact that you have to type in your username twice when logging on to the portal or to the webmail externally. One way to avoid this is just go to the webmail directly using the address http://outlook.com/domain.com, but it might not be the easiest address to remember for your end users.

Luckily enough, this has been sorted out when using the Web Application Proxy in Windows Server 2012 R2. The Web Application Proxy in Server 2012 R2 replaces the ADFS proxy in earlier versions of Windows Server, and have been expanded with functionality to publish other resources in a secure manner as well.

Here we are logging on as usual to mail.office365.com (or portal.microsoftonline.com), and of course the usual redirection is taking place after going to the password field.
2014-02-13 18-06-31

2014-02-13 18-06-52

Earlier, this would have been the place where you were putting in your username once again. Not this time though, your username have now been passed along to the Web Application Proxy and you can continue by just typing in your password!
2014-02-13 18-10-18

Never thought I could be impressed by such a small feature…
I think we’ll see lots of customers upgrading their ADFS infrastructure due to this! 🙂

/Johan

Office 365: Start hybrid migrations and inform users

Lately I’ve been doing quite a few Hybrid setups.
One of the things that cause quite a lot of pain when doing migrations in general is getting information out to your users.
Even if you have a good communication plan, only 20% of your users read the information and only 5% of them reflect and understand it. 🙂

To make this a bit easier, I’ve created a small script that starts Hybrid migration batches and sends out information to the end user.
This is of course just an example, but gives you a good idea of what you are able to do with quite simple means.

HybridMigrationEmail

Getting started:

  • Review all relevant variables in the script
  • Change the text in BodyHybrid.txt to fit in to your needs, the html in that file will be the information sent in the email. The words ActiveSyncDevices,OWAAddress,EmailAddress and Firstname will automatically be replaced by its corresponding variables. (and no the css is not the best 🙂 )
  • Create the PDF Office365Info.pdf and put it in the same folder as the script, that file will be attached to the email.
  • Create input file Prepare-Mailboxes.csv with samAccountNames of the users that you want to migrate. (Note that I am assuming that the users Email Address and UserPrincipalName are the same in the script)
    Also note that the MoveRequest settings in the script might not fit your needs (Baditemlimit, Largeitemlimit).

As we are using the switch -SuspendWhenReadytoComplete in the script, the batches will autosuspend at 95% completion.
That means you’ll have to complete them manually with for example the following line:

Get-CloudMoveRequest | Where-Object {$_.Status -eq "AutoSuspended"} | Resume-CloudMoveRequest. 

Another good option to get in better control of your remote mailbox moves is to use Michael Halls excellent tool based on PowerShell and Excel that does that for you.

Start-HybridMigrations.ps1

<#
.SYNOPSIS
    Starts hybrid migrations based on input (samAccountName) from a csv file.
.DESCRIPTION
    The script starts hybrid migrations and sends out information emails based on the file "BodyHybrid.txt".
    PowerShell V3.0 required
.NOTES
    File Name: Start-HybridMigrations.ps1
    Author   : Johan Dahlbom, johan[at]dahlbom.eu
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

#Import Required Modules
Import-Module ActiveDirectory

#Define Mail Information and Logging
$Users = Get-Content Prepare-MailboxMoves.csv
$BodyHybrid = Get-Content BodyHybrid.txt -Raw
$OWAAddress = 'http://outlook.com/365lab.net'
$Attachment = Get-Childitem .\Office365Info.pdf
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$Logfile = ($PSScriptRoot + "\HybridMigrations.log")

#Define Exchange Online Variables
$ExchServ = "exchangecas.corp.365lab.net"
$HybridServer = "webmail.365lab.net"
$DeliveryDomain = "365lab.mail.onmicrosoft.com"
$OnPremiseUsername = "cloud\svc_mailmigration"
$OnPremisePassword = ConvertTo-SecureString "password" -AsPlainText -Force
$CloudUsername = "svc-cloudmigration@365lab.onmicrosoft.com"
$CloudPassword = ConvertTo-SecureString "password" -AsPlainText -Force
$CloudCred = New-Object System.Management.Automation.PSCredential $CloudUsername, $CloudPassword
$OnPremCred = New-Object System.Management.Automation.PSCredential $OnPremiseUsername, $OnPremisePassword

#Connect to OnPremise Exchange
    if (!(Get-Command Get-ActiveSyncDeviceStatistics -ErrorAction SilentlyContinue)) {
    try {
        Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
    } catch {
            $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchServ/powershell" -Authentication Kerberos
            Import-PSSession -Session $Session -CommandName  Get-ActiveSyncDeviceStatistics -FormatTypeName * | Out-Null
        }
    }
#Connect to Exchange Online
    if (!(Get-Command New-CloudMoveRequest -ErrorAction SilentlyContinue))
    {
	    $session = New-PSSession -ConfigurationName Microsoft.Exchange -Authentication Basic -ConnectionUri https://ps.outlook.com/powershell -AllowRedirection:$true -Credential $CloudCred
	    Import-PSSession $session -Prefix Cloud
    }

function LogWrite {
Param ([string]$Logstring)
Add-Content $Logfile -value $logstring -ErrorAction Stop
Write-Host $logstring
}

function Send-MigrationMail {
param(
[Parameter(Mandatory=$true)]
[string]$Message,
[Parameter(Mandatory=$true)]
[string]$Recipient
)
    $emailFrom = "Office 365 Migration <office365@365lab.net>"
    $smtpserver = "smtpyrelay.365lab.net"
    $subject = "Email Migration Started"
    $cc = "migrator@365lab.net"

    Send-MailMessage -From $emailfrom -To $Recipient -Cc $cc -SmtpServer $smtpserver -Subject $subject -Body $Message -BodyAsHtml -Attachments $Attachment

}
LogWrite "$(get-date -Format u)"
foreach($aduser in $users) {
	$emailAddress = (Get-ADuser $aduser -properties UserPrincipalName).UserPrincipalName
	$firstname = (Get-ADuser $aduser).givenname
        #Find the number of Active ActiveSyncDevices that the user have in the local Exchange solution. If not, set to 0.
		$ActiveSyncDevices = (Get-ActiveSyncDeviceStatistics -Mailbox $aduser | Where-Object {$_.LastSuccessSync -gt (Get-Date).AddDays(-30)}).count
        if (!($ActiveSyncDevices)) {
            $ActiveSyncDevices = "0"
        }
	    #Start Email Migration for user
        try {
            New-CloudMoveRequest -Identity $emailaddress -Remote -RemoteHostName $HybridServer -RemoteCredential $OnPremCred -TargetDeliveryDomain $DeliveryDomain -BatchName $emailAddress -SuspendWhenReadyToComplete -BadItemLimit 50 -LargeItemLimit 50
	} catch {
            $err = $_
            Logwrite "ERROR: Failed to start migration on $($emailAddress)`r`n$($err)"
        }

        #Verify that the move request was successful and send information email
        if ((Get-CloudMoveRequest | Where-Object {$_.BatchName -eq $emailAddress} -ErrorAction SilentlyContinue)) {
            LogWrite "INFO: Started migration of user $($emailAddress) $(get-date -format u)"
            Send-MigrationMail -Recipient $emailAddress -Message $BodyHybrid.Replace('firstname',"$firstname").Replace('ActiveSyncDevices',"$ActiveSyncDevices").Replace("emailaddress","$emailaddress").Replace("OWAAddress","$OWAAddress")
        }else{
            LogWrite "ERROR: Could not start migration for user $($emailAddress), please verify that user exists"
        }
}

The script would of course be possible to extend with lots of things like license assignment, watching the mailboxes as they move and lots and lots of other things, but that’s for another day.
Just let me know if you have suggestions on improvements or other things that should be changed.
Download the script complete with the HTML-template and example csv file from here.

Happy migrations!

/Johan

Managing Office 365 e-mail addresses easy with PowerShell when using DirSync

In most cases you uninstall your local Exchange server after migrating your e-mail to Exchange Online. If you also choose to implement DirSync you place the administration in your local domain instead of the Office 365 Administration Portal.

This means that there is no longer a GUI tool to handle some of the settings related to your mailboxes. Of course you can keep a machine with your old Exchange 2010 Management Console, but often the reason for moving to the cloud is to reduce the complexity and minimize the number of machines.

We don’t have the same problem with other attributes, for example displayname or phone numbers that you’ll find in Users and Computers or the Administrative Center introduced in Windows Server 2008. It does get a bit trickier when you are about to modify primary email address or add alias addresses.

Currently there are no available GUI tools from Microsoft to handle these types of updates easy (but keep your eyes open, things might change 😉 ). However, there are a few third-party tools to achieve this, unless you want to use the Attribute Editor in Users and Computers, or Adsiedit.

Since I’m a big fan of PowerShell I wanted to build a small set of tools to handle simple changes of email addresses. These functions are easy for you to use in your own scripts.

First of all, a function that lists all email addresses of a user:

function Get-O365AliasAddress {
<#
.SYNOPSIS
    Displays all email addresses assigned to a user.
.PARAMETER Identity
    The user to query.
.EXAMPLE
    Get-O365AliasAddress -Identity user01
.EXAMPLE
    Get-ADUser user01 | Get-O365AliasAddress
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user to get addresses of")]
        [string]$Identity
    )

    Import-Module ActiveDirectory

    $result = @()

    (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | foreach {
        $proxy =  $_.split(":")
        $object = New-Object System.Object
        $object | Add-Member –Type NoteProperty –Name Type –Value $proxy[0]
        $object | Add-Member –Type NoteProperty –Name Address –Value $proxy[1]
        $object | Add-Member –Type NoteProperty –Name IsPrimary –Value ($proxy[0] -ceq $($proxy[0].ToUpper()))
        $result += $object
    }
    return $result
}

Next, a function that adds an alias to an existing user

function Add-O365AliasAddress {
<#
.SYNOPSIS
    Adds an alias address to a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to add.
.PARAMETER Type
    The type of address. Default is smtp.
.PARAMETER SetAsDefault
    Indicates if the address should be de default address of the user
.EXAMPLE
    Add-O365AliasAddress -Identity user01 -Address test@365lab.net -SetAsPrimary
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to add")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to add")]
        [string]$Type="smtp",

        [Parameter(
        HelpMessage="Indicates that the address will be set as the default address")]
        [switch]$SetAsDefault
    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()

    $defaultaddress = ''
    $proxyaddresses = ''
    $proxyaddress = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    #If this is the first address, it will be the default
    if ($proxyaddresses.count -eq 0) {
        $SetAsDefault = $true
    }

    if ($SetAsDefault) {

        #New default address to set. Start by removing the old one, but keep it as alias.
        if ($defaultaddress) {
            $pa = $defaultaddress.split(':')
            $newdefaultaddress = "$($pa[0].ToLower()):$($pa[1])"
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses=$defaultaddress} -Add @{proxyaddresses=$newdefaultaddress}
        }

        #Set new default address. In case it already exists, remove it first (it might already be used as alias)
        if ($Type -eq 'SMTP') {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" } -EmailAddress $Address
        } else {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" }
        }

    } else {
        #Just add the new address
        Set-ADUser -Identity $Identity -Add @{proxyaddresses="$($Type):$Address" }
    }
}

Finally we also need a script to delete addresses

function Remove-O365AliasAddress {
<#
.SYNOPSIS
    Removes an alias address from a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to remove.
.PARAMETER Type
    The type of address. Default is smtp.
.EXAMPLE
    Remove-O365AliasAddress -Identity user01 -Address test@365lab.net
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to remove")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to remove")]
        [string]$Type="smtp"

    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()
    $defaultaddress = ''
    $newdefaultaddress = ''
    $proxyaddresses = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    if ($defaultaddress -eq "$($Type):$Address") {
        #We are trying to remove the default address. Now it becomes a bit tricky...
        #First, find the next address of the same type that we can use as default address
        foreach ($proxyaddress in $proxyaddresses) {
            if ($proxyaddress -ne "$($Type):$Address") {
                $newdefaultaddress = $proxyaddress
                break
            }
        }
    }

    #Now we can remove the address
    Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type):$Address"}
    if ($Type -eq 'smtp' -and $defaultaddress -eq "$($Type):$Address") {
        Set-ADUser -Identity $Identity -Clear mail
    }

    if ($newdefaultaddress) {
        #Set $newdefaultaddress as new default address
        Write-Warning "New default address set: $newdefaultaddress"
        Add-O365AliasAddress -Identity $Identity -Address $newdefaultaddress.split(":")[1] -Type $Type -SetAsDefault
    }
}

Now we can play with these functions:

O365AliasAddress

I have put together a PowerShell Module for you to import. Just load it with the Import-Module cmdlet.

Import-Module O365ProxyAddresses

The module can be downloaded here.

I hope that you will find these functions useful in your daily user administration tasks. Please leave your comments below and feel free to suggest improvements.

Happy coding!

/ Andreas

Quick Tip: Using a custom domain to connect to Office 365 Webmail

Office365-sign-inAfter migrating to Office 365 it can be hard to communicate the new webmail addresses to all end-users. Especially when performing a cut-over migration all users suddenly need to be informed about the new address to use.

Office 365 uses many different host names, for example portal.microsoftonline.com and login.microsoftonline.com, both taking you to the portal page where users can access all services or download desktop software. But you still have to use the Outlook link in the portal page to access your mail. How can we make this simpler for the end-users?

If your tenant is set up with ADFS you want to use the address outlook.com/yourdomain.com. This automatically logs you on directly to the webmail using your domain credentials, allowing single sign-on to Office 365. Unfortunately with a custom domain this can only be achieved using a local web server that handles the redirect to the correct web page.

So, what to do then? I don’t want to set up a web server just to handle a redirect. Luckily there is an easy workaround to this: Use DNS to create a CNAME (for example mail.mydomain.com) that point to mail.office365.com. This presents a login screen for the users, and they will then be logged on directly to the webmail page.

The CNAME can be created using PowerShell cmdlets for DNS, which was introduced in Windows Server 2012:

Add-DnsServerResourceRecordCName -HostNameAlias mail.office365.com -Name mail -ZoneName mydomain.com

If you are using ADFS the users must check the “Keep me signed in” checkbox to handle single sign-on in the future.

/ Andreas

Mail flow rules for alias email addresses in Exchange Online

Stumbled upon an ‘issue/feature’ with mail flow rules (transport rules) that I’ve encountered before a couple of days ago and thought it was a good idea sharing.
It’s always good to get a reminder of things from time to time, even if it might be a bit obvious 🙂
The ‘issue’ do of course apply to Exchange On premise environments as well.

Case:
The user Kalle Kula have three email addresses as following:
SMTP:kalle.kula@corp.365lab.net (primary)
smtp:kalle.kula@spam.365lab.net
smtp:kalle.kula@365lab.onmicrosoft.com

If an email is sent to the address kalle.kula@spam.365lab.net (one of the alias email address for the user), we want to append a disclamer that states “This email was sent to the domain spam.365lab.net”

How to do it:
1. In Exchange Admin Center, under mail flow -> rules, create a new dislaimer rule.
2014-01-02 13-18-37
2. To be able to do more granular selection and actions, click “More Options” in the bottom left corner.

2014-01-02 13-22-46
3. Then create your rule with the following options.

The logical thing here would have been to apply the rule if the recipient address matches my particular address, but that does only work for primary email addresses. So therefore we need to apply it on the header “To” and match the text pattern kalle.kula@spam.365lab.net (the alias address)

2014-01-02 13-36-072014-01-02 13-37-172014-01-02 13-39-00  

Add your disclaimer and set fall back action.
2014-01-02 13-42-27
The only thing you need to do now for the policy to be applied on future emails is to choose mode: Enforce, save it and you’re done!

If you want to check whether a rule has been used or not, you can use EAC as well.
2014-01-02 14-07-11

PowerShell version:
To do the same as above with PowerShell, you can use the following PowerShell lines:

 
#Connect to Exchange Online with PowerShell
$cred = Get-Credential
$O365 = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $cred -Authentication Basic -AllowRedirection 
$importcmd = Import-PSSession $O365 
#Create the Mail Flow rule (transport rule)
New-TransportRule -Name "Disclaimer - spam.365lab.net" `
                   -HeaderMatchesPatterns {kalle.kula@spam.365lab.net} `
                   -HeaderMatchesMessageHeader To `
                   -ApplyHtmlDisclaimerText "This email was sent to the domain spam.365lab.net" `
                   -ApplyHtmlDisclaimerLocation Append `
                   -ApplyHtmlDisclaimerFallbackAction Wrap `
                   -Mode Enforce 

Further documentation on mail flow rules (transport rules) you find on http://technet.microsoft.com/en-us/library/dd351127(v=exchg.150).aspx.

/Johan

Emails from Messagelabs(Symantec) blocked after running HCW (Hybrid Configuration Wizard)

A couple of my customers have been running in to this issue lately, so I thought it was a good idea to share the issue with you guys.

Note: the issue only applies to those that are using Messagelabs/Symantec as their current spam-provider. (MX-record pointing to messagelabs)

Issue
After running Hybrid Configuration Wizard in Exchange 2010/2013 you experience intermittent issues with incoming emails from the internet (Messagelabs). If you dig further down to the issue you come to the conclusion that it only happens when you receive emails from certain ip subnets that Messagelabs are using.

Cause
As part of the Hybrid Configuration Wizard process, a receive connector named “Inbound from Office 365” is created. The purpose of that receive connector is to enable a secure mail flow directly from Office 365.
For that reason, the connector is configured with the setting RequireTLS set to True .
It also configures a set of remote ip ranges that the connector accepts (public Office 365 IP-ranges).

And it was here I came across the actual cause to this issue.
HCW are adding a remote ip range that overlaps with the public IP-ranges Messagelabs are using. This in combination with the fact that Messagelabs obviously doesn’t fully support TLS, makes this issue happen.
I confirmed this both with the transport logs and in Messagelabs documentation (http://images.messagelabs.com/EmailResources/ImplementationGuides/Subnet_IP.pdf).
The overlapping subnet is 67.219.240.0/20.

See screenshot from the Exchange Server and Symantecs documentation below:
2013-12-25 22-34-08

2013-12-25 22-34-55

Looking further into the documentation Microsoft have on this at http://help.outlook.com/en-us/140/gg263350.aspx, you find out that the subnet 67.219.240.0/20 isn’t there.
That is also confirmed by doing a whois on the subnet (http://www.whois.net/ip-address-lookup/67.219.240.0) – this subnet does not belong to Microsoft.

That means it should be perfectly safe to remove that subnet from the receive connector, and I can confirm that it hasn’t created any problems in the cases I bumped in to.

Solution(or workarounds…)
The quick and dirtly solution to this issue is to to turn off the TLS requirements on the receive connector “Inbound from Office 365” with the PowerShell command as below

Get-ReceiveConnector "Inbound from Office 365" | Set-ReceiveConnector -RequireTLS $false

The better solution is to just remove the subnets from the receive connector, and it should be perfectly safe to do this since no emails from Office 365 will arrive that way.

Hope this helps you out if you are running in to this issue!

/Johan