Using Powershell to import contacts into Exchange and Outlook Live
When performing migrations between different systems, there’s always the case where the tools available don’t do the job out of the box – and although IMAP migration tools for Exchange and Outlook Live can be great for moving mail, there isn’t a decent free solution for importing contacts.
For a recent migration from a Unix system (Dovecot + SquirrelMail) to Outlook Live, I came across this very scenario. While Microsoft provide IMAP migration tools to move mail to Outlook Live (which I was lucky enough to beta test before it was widely available), no tools are provided to move other data such as contacts and calendars.
While this isn’t the actual code I used (I wrote my original code in C#), I’ve re-visited what I’ve done and listened to what others say they need. What I’ve heard is it would be useful to have some Powershell code that out-of-the box can import contacts into any Exchange mailbox. The reason that Powershell is the right language for code like this is that it’s fairly easy for the enterprising administrator to modify to their needs.
Getting started
Before you can run this script, there are a few things you need to set up first. Don’t worry though! It’s nothing too onerous – all is needed to get going is two things:
- Setup of EWS impersonation, unless you only want to work with accounts you know the username/password for.
- Installing the Managed API DLL onto your computer.
Setup of Exchange Web Services Impersonation
Exchange Web Services is the programmatic interface to each user’s Exchange mailbox. Most actions that can be performed in Outlook can be performed via EWS – so much so, in fact, that Apple’s Mail.app in Snow Leopard and the latest update for Entourage 2008 use EWS as the backend for the mail clients themselves.
If you’re planning on doing a mass-import of contacts, or don’t know the user’s password you’re importing, then setup of EWS impersonation is the step you need to take to allow a trusted account to switch to the user you want to import.
In Exchange 2010, setup of Exchange impersonation is managed via RBAC. Full details of how to setup impersonal are on the Microsoft site, but if you just want to get it setup for a single admin/service account, org-wide, use the following command substituing serviceaccount for your service account:
On Exchange 2007, it’s very different, and is managed by AD permissions on individual Client Access Servers. Again – full details on the Microsoft site, but to add the permission to all CAS servers, the following code will suffice, again substituting serviceaccount for your Service account:
Installing the Exchange Web Services Managed API
Next, whatever environment you are in, you need a copy of the DLL file that provides the bits and pieces that make this all work. The easiest way to get up and running is to download the API (choosing the right version for your platform), and install it to it’s default location.
Download the EWS Managed API from the Microsoft site
Once downloaded and installed, you should be good to go with script.
Using the script Import-MailboxContacts.ps1
So, you’ve got impersonation setup if needed, got the EWS DLL on your machine. You should be good to go!
For each mailbox you want to import contacts from you need a separate CSV file. The format of the file is intentionally the same as Outlook will export in, mainly because a lot of apps already export in the format (Gmail does, for example) so you should find it easy to get test data, but if you haven’t I’ve included a sample file to get you started.
Along with the data, you also need, at a minimum, the mailbox email address to import to. This is used when impersonating, and for Autodiscover. If you’re not logged on as the user you want to use for the import, you can specify a username and password. Additionally, there’s a variety of options to specify the EWS Url manually, switching to the Exchange 2007 version of the API etc.
Examples
The first example is a straightforward import into the current logged-on user’s mailbox. We’re specifying the Email Address and the URL to EWS (to bypass Autodiscover):
As you can see in the above example – it’s all fairly straightforward. You’ll also see the output shows the contacts that have been created – you can use standard Powershell pipeling to export this data, pipe it to another command or filter it.
The second example is slightly more complicated, and reflects the main use case. We’re connecting to a remote exchange organisation, using Autodiscover to find the EWS address, enabling impersonation and providing credentials at the command line:
In addition to the options above, you can also specify the parameters -Exchange2007 $true and use -EWSManagedApiDLLFilePath to specify a different path the the EWS DLL.
Powershell Code
Finally, you’ll need a copy of the code to do all this. At the bottom of the post you’ll find a ZIP file containing the Import-MailboxContacts.ps1 file and a sample CSV file, and below you’ll see the code itself. Happy importing!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | param([string]$CSVFileName,[string]$EmailAddress,[string]$Username,[string]$Password,[string]$Domain,[bool]$Impersonate,[string]$EwsUrl,[string]$EWSManagedApiDLLFilePath,[bool]$Exchange2007); # # Import-MailboxContacts.ps1 # # By Steve Goodman, Use at your own risk. # # Parameters # Mandatory: # -CSVFileName : Filename of the CSV file to import contacts for this user from. Same format as Outlook Export. # -EmailAddress : Account SMTP email address. Required, but only used when impersonating or with Autodiscover - otherwise uses the user you login as # Optional: # -Impersonate : Set to $true to use impersonation. # -Username : The username to use. If this isn't specified (along with Password), attempts to use the logged on user. # -Password : Used with above # -Domain : Used with above - optional. # -EwsUrl : The URL for EWS if you don't want to use Autodiscover. Typically https://casserver/EWS/Exchange.asmx # -EWSManagedApiDLLFilePath : (Optional) Overwrite the filename and path to the DLL for EWS Managed API. By default, uses the default install location. # -Exchange2007 : Set to $true to use the Exchange 2007 SP1+ version of the Managed API. # # Contact Mapping - this maps the attributes in the CSV file (left) to the attributes EWS uses. # NB: If you change these, please note "First Name" is specified at line 102 as a required attribute and # "First Name" and "Last Name" are hard coded at lines 187-197 when constructing NickName and FileAs. $ContactMapping=@{ "First Name" = "GivenName"; "Middle Name" = "MiddleName"; "Last Name" = "Surname"; "Company" = "CompanyName"; "Department" = "Department"; "Job Title" = "JobTitle"; "Business Street" = "Address:Business:Street"; "Business City" = "Address:Business:City"; "Business State" = "Address:Business:State"; "Business Postal Code" = "Address:Business:PostalCode"; "Business Country/Region" = "Address:Business:CountryOrRegion"; "Home Street" = "Address:Home:Street"; "Home City" = "Address:Home:City"; "Home State" = "Address:Home:State"; "Home Postal Code" = "Other:Home:PostalCode"; "Home Country/Region" = "Address:Home:CountryOrRegion"; "Other Street" = "Address:Other:Street"; "Other City" = "Address:Other:City"; "Other State" = "Address:Other:State"; "Other Postal Code" = "Address:Other:PostalCode"; "Other Country/Region" = "Address:Other:CountryOrRegion"; "Assistant's Phone" = "Phone:AssistantPhone"; "Business Fax" = "Phone:BusinessFax"; "Business Phone" = "Phone:BusinessPhone"; "Business Phone 2" = "Phone:BusinessPhone2"; "Callback" = "Phone:CallBack"; "Car Phone" = "Phone:CarPhone"; "Company Main Phone" = "Phone:CompanyMainPhone"; "Home Fax" = "Phone:HomeFax"; "Home Phone" = "Phone:HomePhone"; "Home Phone 2" = "Phone:HomePhone2"; "ISDN" = "Phone:ISDN"; "Mobile Phone" = "Phone:MobilePhone"; "Other Fax" = "Phone:OtherFax"; "Other Phone" = "Phone:OtherTelephone"; "Pager" = "Phone:Pager"; "Primary Phone" = "Phone:PrimaryPhone"; "Radio Phone" = "Phone:RadioPhone"; "TTY/TDD Phone" = "Phone:TtyTddPhone"; "Telex" = "Phone:Telex"; "Anniversary" = "WeddingAnniversary"; "Birthday" = "Birthday"; "E-mail Address" = "Email:EmailAddress1"; "E-mail 2 Address" = "Email:EmailAddress2"; "E-mail 3 Address" = "Email:EmailAddress3"; "Initials" = "Initials"; "Office Location" = "OfficeLocation"; "Manager's Name" = "Manager"; "Mileage" = "Mileage"; "Notes" = "Body"; "Profession" = "Profession"; "Spouse" = "SpouseName"; "Web Page" = "BusinessHomePage"; "Contact Picture File" = "Method:SetContactPicture" } # CSV File Checks # Check filename is specified if (!$CSVFileName) { throw "Parameter CSVFileName must be specified"; } # Check file exists if (!(Get-Item -Path $CSVFileName -ErrorAction SilentlyContinue)) { throw "Please provide a valid filename for parameter CSVFileName"; } # Check file has required fields and check if is a single row, or multiple rows $SingleItem = $false; $CSVFile = Import-Csv -Path $CSVFileName; if ($CSVFile."First Name") { $SingleItem = $true; } else { if (!$CSVFile[0]."First Name") { throw "File $($CSVFileName) must specify at least the field 'First Name'"; } } # Check email address if (!$EmailAddress) { throw "Parameter EmailAddress must be specified"; } if (!$EmailAddress.Contains("@")) { throw "Parameter EmailAddress does not appear valid"; } # Check EWS Managed API available if (!$EWSManagedApiDLLFilePath) { $EWSManagedApiDLLFilePath = "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll" } if (!(Get-Item -Path $EWSManagedApiDLLFilePath -ErrorAction SilentlyContinue)) { throw "EWS Managed API not found at $($EWSManagedApiDLLFilePath). Download from http://bit.ly/9O7gLo"; } # Load EWS Managed API [void][Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"); # Create Service Object if ($Exchange2007) { $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1) } else { $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010) } # Set credentials if specified, or use logged on user. if ($Username -and $Password) { if ($Domain) { $service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials($Username,$Password,$Domain); } else { $service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials($Username,$Password); } } else { $service.UseDefaultCredentials = $true; } # Set EWS URL if specified, or use autodiscover if no URL specified. if ($EwsUrl) { $service.URL = New-Object Uri($EwsUrl); } else { try { $service.AutodiscoverUrl($EmailAddress); } catch { throw; } } # Perform a test - try and get the default, well known contacts folder. if ($Impersonate) { $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress); } try { $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts); } catch { throw; } # Add contacts foreach ($ContactItem in $CSVFile) { # If impersonate is specified, do so. if ($Impersonate) { $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress); } $ExchangeContact = New-Object Microsoft.Exchange.WebServices.Data.Contact($service); if ($ContactItem."First Name" -and $ContactItem."Last Name") { $ExchangeContact.NickName = $ContactItem."First Name" + " " + $ContactItem."Last Name"; } elseif ($ContactItem."First Name" -and !$ContactItem."Last Name") { $ExchangeContact.NickName = $ContactItem."First Name"; } elseif (!$ContactItem."First Name" -and $ContactItem."Last Name") { $ExchangeContact.NickName = $ContactItem."Last Name"; } $ExchangeContact.DisplayName = $ExchangeContact.NickName; $ExchangeContact.FileAs = $ExchangeContact.NickName; $BusinessPhysicalAddressEntry = New-Object Microsoft.Exchange.WebServices.Data.PhysicalAddressEntry; $HomePhysicalAddressEntry = New-Object Microsoft.Exchange.WebServices.Data.PhysicalAddressEntry; $OtherPhysicalAddressEntry = New-Object Microsoft.Exchange.WebServices.Data.PhysicalAddressEntry; # This uses the Contact Mapping above to save coding each and every field, one by one. Instead we look for a mapping and perform an action on # what maps across. As some methods need more "code" a fake multi-dimensional array (seperated by :'s) is used where needed. foreach ($Key in $ContactMapping.Keys) { # Only do something if the key exists if ($ContactItem.$Key) { # Will this call a more complicated mapping? if ($ContactMapping[$Key] -like "*:*") { # Make an array using the : to split items. $MappingArray = $ContactMapping[$Key].Split(":") # Do action switch ($MappingArray[0]) { "Email" { $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::($MappingArray[1])] = $ContactItem.$Key; } "Phone" { $ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::($MappingArray[1])] = $ContactItem.$Key; } "Address" { switch ($MappingArray[1]) { "Business" { $BusinessPhysicalAddressEntry.($MappingArray[2]) = $ContactItem.$Key; $ExchangeContact.PhysicalAddresses[[Microsoft.Exchange.WebServices.Data.PhysicalAddressKey]::($MappingArray[1])] = $BusinessPhysicalAddressEntry; } "Home" { $HomePhysicalAddressEntry.($MappingArray[2]) = $ContactItem.$Key; $ExchangeContact.PhysicalAddresses[[Microsoft.Exchange.WebServices.Data.PhysicalAddressKey]::($MappingArray[1])] = $HomePhysicalAddressEntry; } "Other" { $OtherPhysicalAddressEntry.($MappingArray[2]) = $ContactItem.$Key; $ExchangeContact.PhysicalAddresses[[Microsoft.Exchange.WebServices.Data.PhysicalAddressKey]::($MappingArray[1])] = $OtherPhysicalAddressEntry; } } } "Method" { switch ($MappingArray[1]) { "SetContactPicture" { if (!$Exchange2007) { if (!(Get-Item -Path $ContactItem.$Key -ErrorAction SilentlyContinue)) { throw "Contact Picture File not found at $($ContactItem.$Key)"; } $ExchangeContact.SetContactPicture($ContactItem.$Key); } } } } } } else { # It's a direct mapping - simple! if ($ContactMapping[$Key] -eq "Birthday" -or $ContactMapping[$Key] -eq "WeddingAnniversary") { [System.DateTime]$ContactItem.$Key = Get-Date($ContactItem.$Key); } $ExchangeContact.($ContactMapping[$Key]) = $ContactItem.$Key; } } } # Save the contact $ExchangeContact.Save(); # Provide output that can be used on the pipeline $Output_Object = New-Object Object; $Output_Object | Add-Member NoteProperty FileAs $ExchangeContact.FileAs; $Output_Object | Add-Member NoteProperty GivenName $ExchangeContact.GivenName; $Output_Object | Add-Member NoteProperty Surname $ExchangeContact.Surname; $Output_Object | Add-Member NoteProperty EmailAddress1 $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] $Output_Object; } |
Checking for unmanaged Live IDs in Outlook Live
This isn’t a post for everyone – it’s aimed at Administrators of a Live@EDU / Outlook Live environment who are looking to find existing unmanaged EASI IDs / Windows Live IDs on the domain they’ve brought onto Outlook Live.
If you’re using automated provisioning you’ll find this can be a problem as when you use the New-Mailbox cmdlet it will fail, and you’ll have to re-run it with either the –ImportLiveID or –EvictLiveID options. This script allows you to get a CSV input file, with the column Username and generate a list of the status of the Live IDs so you can embark on your batch creation knowing where you stand.
Usage: .\Check-Unmanaged.ps1 -AdminUsername admin@uni.edu -AdminPassword Pa$$w0rd -InputFile .\input.csv -OutputFile .\output.csv
The -AdminUsername is typically you’re Outlook Live admin. It needs to be able to connect to the Windows Live Admin web service .-OutputFile is optional - results are printed to the screen also. For each user in your domains you will see either InUse for managed IDs, InUseUnmanaged for IDs you need to evict/import to use and Available for IDs not in use.
The included DLL, ManageDomain2.dll is required. This is auto-generated from the WSDL. If you wish to generate your own you need Visual Studio 2008 or similar. See the source of the ps1 file for the two-lines you need to re-generate the DLL - it's just a proxy web service library.
(NB. No warranty is provided with this script, it is provided as-is and any damage it may do is your responsibility. Please use on your own test environment first. That said - apart from connecting to the service, the main method used is GetMemberNameState)
Download CheckUnmanaged.zip
Writing Powershell scripts that target Exchange 2007 and 2010
If you’re planning a migration to Exchange 2010 that won’t be completed overnight, you may want to be able to run Exchange Powershell scripts that execute both Exchange 2007 and 2010 cmdlets from the same script. There’s a few reasons for doing this, including:
- If you use logic to determine where to place new user mailboxes, and some mailboxes will be on 2007 and some on 2010.
- If you create new mailboxes on Exchange 2007 and then add the new mailbox to a Exchange 2010 distribution group (for example, if you use moderated distribution groups).
- If you’re using Outlook Live (Live@EDU hosted Exchange 2010) and create MailUsers on-premise and Mailboxes in the hosted environment.
- You are using the Transporter suite to migrate mail and need to create mailboxes on a temporary Exchange 2007 server, migrate mail, and then move mailboxes to Exchange 2010.
OK – so my examples are mostly about provisioning and these are real-life examples of where I’ve used this method, but I’m sure you can think of other examples where it might be useful to you. So – what do you need?
- Exchange 2007 SP2 management tools on any OS that supports them.
- Powershell 2.0
- Remote Powershell enabled on the account you run the script as (using Set-User username –RemotePowerShellEnabled:$true)
You can use this on Windows Server 2003 R2, x86; no Exchange 2010 management tools are required. Enough of what you need – here’s what you need to put in your Powershell scripts to do Exchange 2007/2010:
# Add Exchange 2007. If you'll always run from the Exchange Management Shell, you don't need the next line.Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin# Exchange 2007 Commands Go Here - i.e.Get-MailboxDatabase# Unload Exchange 2007Remove-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin# Create Remote Powershell session with Exchange 2010 - edit the server name in ConnetionUri$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange.contoso.com/powershell/ -Authentication KerberosImport-PSSession $Session# Exchange 2010 Commands Go Here - i.e.Get-MailboxDatabase# Unload Exchange 2010Remove-PSSession $Session
If you’re looking to connect to Outlook Live instead of Exchange 2010, simply replace the line beginning “$Session =” with a couple of lines similar to this:
$LiveCred = New-Object System.Management.Automation.PSCredential "yourliveadmin@contoso.edu", (ConvertTo-SecureString "password" -AsPlainText -Force)$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection -WarningAction SilentlyContinue
Hope this helps!
Steve
Scripted Shared Mailbox Creation on Exchange 2007/2010
I thought I’d share one of the scripts we use for automating creation of Shared Mailboxes within our organisation. One of the more laborious parts of creating shared mailboxes after creation of the actual Mailbox, is setting the manager, department, and granting a (possibly long) list of users permissions and send-as rights.
This script is a simple Powershell script that takes some of the pain out of the process. The assumption is that you want to achieve the following:
- Create a shared mailbox with a common alias, email address and username, checking input values before attempting to create the mailbox.
- You would like to specify the Department at creation and use the Department AD attribute to generate Address Lists
- You would like to attach a user as Manager for the Shared Mailbox so it is clear who manages it
You can use the script with Parameters – e.g:
.\New-SharedMailbox.ps1 -Department "Human Resources" -Alias hr_test -DisplayName "HR Test" -Manager managerusername -Usernames "username1,username2"
–Department is the text to use for the department attribute
–Alias is the Email Alias & Username
–DisplayName is the friendly textual name shown to users and recipients
–Manager is the username, UPN or email address of the Manager (who will have full access + send as rights along with being set as Manager)
–Usernames is a comma separated list of usernames of people who should also have full access and send-as rights.
Or - if that isn’t your thing, then you can just run the script without any options, and it will prompt you as you go along for the parameters. If you are using the Department attribute for Address List generation, you can hit enter when prompted for the department to get a list of in-use departments. As you go along values will be checked, to ensure the Alias and Display Name aren’t in use by Mailboxes or Mail Users and that the Manager and Usernames are attached to valid Mailboxes.
Before you get started with script it does need a little setup.
The setup is fairly straightforward, though. Open the script in a text editor, and change the values for $DomainController, $OU, $UPNDomain and $MailboxDatabase to values appropriate for your organisation. It goes without saying you should test this out on something not connected to your production environment. I won’t take any responsibility for any damage you do with this!
I’ve obviously taken some stuff out that is specific to our organisation (for example, we have a switch statement to determine the mailbox database and exchange server; and also generate Unix information) and if this almost does what you want let me know and I’ll be happy to help you customise it.
Download New-SharedMailbox.ps1
Exchange – Bulk-add forwarding addresses
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