There’s not many circumstances where you’d want to set forwarding on lots of mailboxes at once – but if you’re in that situation (perhaps you’re moving mailboxes to a Cloud mail provider) it’s useful to know that it’s fairly straightforward and can be accomplished with a little PowerShell.
In the example below, we’re assuming that your on-premise Exchange 2007/2010 domain is contoso.com and all your mailboxes are in the Staff OU.
The Cloud provider you’re moving your mail to will eventually take over the MX record for your domain, but will also accept mail to each user’s Username@cloud.contoso.com. We’ll keep mail contacts used for the forwarding in the OU CloudContacts.
To accomplish this, we quite simply need to get the mailboxes in the specific OU, create a mail contact for each one (with the external email address they will use on the cloud provider’s domain) and then set the forwarding address on the mailbox:
# Loop though the object returned by Get-Mailbox with each element represented by $mailbox
foreach ($mailbox in (Get-MailBox -ResultSize Unlimited -OrganizationalUnit contoso.com/Staff)
{
# Create the forwarding address string $ForwardingAddress= $mailbox.SamAccountName + "@cloud.contoso.com"
# Check there isn't a contact, then add one
If (!(Get-MailContact $ForwardingAddress -ErrorAction SilentlyContinue))
{
New-MailContact $ForwardingAddress-ExternalEmailAddress $ForwardingAddress- OrganizationalUnit contoso.com/CloudContacts }
# Set the forwarding address Set-Mailbox $mailbox -ForwardingAddress $ForwardingAddress
}
Of course, that’s a fairly simple example. What if the email addresses at the cloud provider don’t match the Windows Logon ID or anything else that is an attribute of the Mailbox? Well the simple solution is to use something like a CSV file containing a mapping between an attribute on each Mailbox and the external email address for that user:
SamAccountName,ForwardingAddress jamesw,jimbo@cloud.contoso.com philipg,phil@cloud.contoso.com
In this example, we’ll save that file as input.csv and use that as are input to the foreach loop. Again CloudContacts OU will contain our new mail contact objects:
# Loop through the object returned by Import-Csv with each element represented by $person
foreach ($person in (Import-Csv .\input.csv))
{ # Check the Mailbox for the person exists If ((Get-Mailbox $person.SamAccountName))
{ # Check the mail contact doesn't exist and if not add it If (!(Get-MailContact $person.ForwardingAddress))
{
New-MailContact $person.ForwardingAddress -OrganizationalUnit contoso.com/CloudContacts }
# Set the Forwarding Address on the Mailbox Set-Mailbox $person.SamAccountName -ForwardingAddress $person.ForwardingAddress }
}
Word of caution – it’s worth (as always) running these commands in your test environment first – and of course consider appending -WhatIf to the New-MailContact and Set-Mailbox commands to check they’ll do what you want in your production environment.
Hope this helps,
Steve
just wanted to say thanks. This is a one-time migration for us so I modified Cody’s script a bit. I dropped some of the fields for the contacts. and for some reason had to remove the ‘-‘ in front of Set-MailContact for it to work. And I turned off delivery to both boxes.
My CSV looks like
name,alias,email
User, test.user, external@externaladdress.com
and the PS1 script looks like-
Import-Csv ‘input.csv’ | `
ForEach{
New-MailContact $_.name `
-organizationalunit “OU=TempContacts,DC=yourdomain,DC=local” `
-Alias $_.alias `
-ExternalEmailAddress $_.email
Set-MailContact -identity $_.alias -HiddenFromAddressListsEnabled $True -EmailAddressPolicyEnabled:$false
Set-Mailbox -identity $_.alias -ForwardingAddress $_.email -DeliverToMailboxAndForward:$False
}
I couldn’t get the CSV version of the script on your blog to work at all. Even after changing it drastically and having another scripting expert help me with it. So I made some changes to a simple script I came up with a long time ago. Not sure why everyone tries to do something so simple with such advanced/complex scripts.
This works…
Import-Csv ‘forwardtest.csv’ | `
ForEach{
New-MailContact $_.name `
-DisplayName $_.name `
-FirstName $_.fname `
-LastName $_.lname `
-organizationalunit “OU=tempusercontactsou,OU=Contacts,DC=domain,DC=local” `
-Alias $_.alias `
-ExternalEmailAddress $_.email
-Set-MailContact -identity $_.alias -HiddenFromAddressListsEnabled $True -EmailAddressPolicyEnabled:$false
Set-Mailbox -identity $_.alias -ForwardingAddress $_.email -DeliverToMailboxAndForward:$True
}
The contacts will be hidden and I will use them to forward email to the mailbox account in Exchange referenced by “alias.” Also, your default email policy may tack on additional SMTP addresses in *addition* to the external email address. I’ve tested the above and even with the policy being disabled it still seems to append SMTP addresses to the contacts. Easy fix would be to use ADModify and remove *@domain.local and @domain.com from the contacts. Keep the switch for disabling the policy off in the script or the SMTP addresses will just pop back into the contacts.
The duplicate alias for the contact and the email enabled user account won’t matter.
Example CSV file:
name,fname,lname,alias,email
Test User, Test, User, test.user, external@externaladdress.com
FYI – It generates an error stating the object couldn’t be found (the external contact) and then it prompts “ExternalEmailAddress:” If you fill in an email address into that prompt in the PowerShell and hit enter, it actually creates a new contact with the address you’ve punched in and sets forwarding. For some reason the script isn’t using the info in the CSV to create the contact. If I figure this out I’ll post back so you can fix your script above..
I’m trying to use the CSV method and when it’s run it reports that the forwarding address doesn’t exist on the domain controller. It seems as if the script doesn’t create the contact, it expects the contact to already exist.
There were some definite issues with the script on Exchange 2007, but the command shell did a great job of telling me where they were.
The two easiest problems with your script to find were:
– Line 2 is missing a “)” at the end.
– Line 9 shows the “-” after the $Forwarding Address and before the space, when it should be before the OrganizationalUnit.
I made some changes to the script, primarily to make the contacts easier to view under Mail Contacts in exchange.
I added the piece to hide the address from the GAL and another piece to force Delivery to both mailboxes.
Below is the fully functioning script (anything between _ is where you customize it)
# Loop though the object returned by Get-Mailbox with each element represented by $mailbox
foreach ($mailbox in (Get-MailBox -ResultSize Unlimited -OrganizationalUnit _contoso.com/Staff_))
{
# Create the forwarding address string
$ForwardingAddress= $mailbox.SamAccountName + “_@googledomain.org_”
# Check there isn’t a contact, then add one
If (!(Get-MailContact $ForwardingAddress -ErrorAction SilentlyContinue))
{
New-MailContact $mailbox.displayName-ExternalEmailAddress $ForwardingAddress -OrganizationalUnit _test.domain/GoogleContacts_ | Set-MailContact -HiddenFromAddressListsEnabled $true
}
# Set the forwarding address
Set-Mailbox -Identity $mailbox.SamAccountName -ForwardingAddress $mailbox.displayName -DeliverToMailboxAndForward $true
}
Save this file as a .ps1 and then run it from the Exchange Command Shell. For Testng purposes I replaced “-OrganizationalUnit _contoso.com/Staff_” with “-Identity _testusername_.
nice.. Found out that by using $mailbox.displayName as the contact name was causing the forward to infinite loop and try and forward to my internal mailbox by default. Because of this, I had to change the script after the If statement. Notice that I added the Alias.
New-MailContact “$ForwardingAddress”-ExternalEmailAddress $ForwardingAddress -OrganizationalUnit _test.domain/GoogleContacts_ -Alias $mailbox.SamAccountName | Set-MailContact -HiddenFromAddressListsEnabled $true
}
# Set the forwarding address
Set-Mailbox -Identity $mailbox -ForwardingAddress $ForwardingAddress -DeliverToMailboxAndForward $true
}
Any chance you could update my original post with this new change Steve? I found your blog to be very useful and I am sure others will as well.
Thanks this worked a treat!
I keep getting “get-mailbox is an unknown comandlet”
what am I doing wrong?
nevermind…. foranyone else doing this, who has like me never doen it before…
1) there are a number of typos in this script, you will need to debug (I am not ungrateful, yhis is a FANTASTIC starting point, but it needs attention)
and
2) you must run it in the exchange shell, which I did not realize is ALSO powershell, but with the exchange commandlets installed.
Where’s the typo? Are you sure it’s because you didn’t use the Exchange Management Shell, which is really if you are inexperienced with PowerShell I would recommend you run scripts from?
Steve
Steve,
please don’t get the wrong idea. I could not have written this script from scratch. I owe you a great deal and am very grateful for your work.
Your right, this is my first powershell experience. I normally work with bash, and so I am feeling my way slowly here so as not to make assumptions. It took me three tries to figure out the difference between powershell and exchange management shell (commandlets) which is why i put it in the comments for others.
The typo I found was (I am only working with the original script not using a csv file) that there was a missing ) at the end of the foreach line.
I found another difference but it might be a version difference and not a typo. On my 2007exchange box (running on a 03sp2 server) it wants a -Name entry for the -NewMailContact line.
also, it does not like the leading $forwradingaddress before the -externalemailaddress option call. not sure why.
Im still playing with it. Ill post what works for me when im done. maybe the rest are errors I introduced with copy/paste because they appear to be missing spaces.
thank you again for your work on this.
Josh
How can this script (the first one) be modified to add a contact with the name firstname.lastname@domain.com?
amit, to get first.last you need to make the line regarding $forwardingaddress read something like
$ForwardingAddress= $mailbox.Name + “.” + $mailbox.sn + “@cloud.contoso.com”
youll have to play with it to find the ldap attribute that holds the first and last name in your environment, but…. thats the idea….. replace $mailbox.SamAccount with the proper attributes.
have fun and try not to take the server down/
Great script. 2 suggestions that I will put out there that could help some folks depending on their scenario.
You might want to hide the newly created contacts from the GAL, so you can modify the New-MailContact line to be: New-MailContact $ForwardingAddress -ExternalEmailAddress $ForwardingAddress -OrganizationalUnit ‘contoso.com/Cloud’ | Set-MailContact -HiddenFromAddressListsEnabled $true
You might also want to have the mail dual-delivered (I.E. delivered to the exchange mailbox AND the cloud). This would involve modifying the Set-Mailbox line to: Set-Mailbox $mailbox -ForwardingAddress $ForwardingAddress -DeliverToMailboxAndForward:$true
Thanks again for sharing your work!