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.1\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; } |
Related posts:
- How to export Email Headers from Exchange using Powershell
- Managing Office 365 and On-Premises Exchange 2010 from the same Powershell Session
- Set up Federated Free/Busy and Calendar Sharing between Exchange 2010 SP1 and Outlook Live [Updated]
- Report your Exchange Offline Address Book Sizes using Powershell
- Writing Powershell scripts that target Exchange 2007 and 2010



August 24th, 2010 - 01:41
Great information! I’ve been looking for something like this for a while now. Thanks!
September 8th, 2010 - 21:54
In your example code, there doesn’t appear to be a way to deal with groups of contacts that the user may have created in the original address book. Would you happen to know the format of the attributes on the EWS side that would be used for group entries in a user’s contacts?
September 8th, 2010 - 22:13
Hi Kurt,
Nope, it doesn’t – is this something you would like? I can look at adding it if it will help you. I don’t know of the top of my head what would be needed to make it work but happy to find out.
Steve
September 10th, 2010 - 16:31
I wouldn’t worry too much about it, as we’re treating it as a “would be nice” kind-of thing, with just getting the contacts transferred being the main focus. If you happen to come up with something, that would be great.
Thanks,
Kurt
October 5th, 2010 - 19:00
Group of contacts is something I’d definitely be interested in knowing about.
October 13th, 2010 - 11:01
I will look into doing a V2.0.
October 12th, 2010 - 13:27
I don’t suppose anyone could provide a web page for users to pass the appropriate variables into this script for ‘self service’
ie: a browse button to the .csv file, username field, email address field password field etc.
and have all of that passed to the powershell script so users can import their own contacts…
Thanks for any help.
October 13th, 2010 - 11:01
If you have ASP.NET experience, I have a C# version that also does calendar items (in fact the code was translated to Powershell from my C# original). Would this be of any use?
October 13th, 2010 - 15:16
no C# unfortunately, any other ideas? I just downloaded powergui to see if that can make it simple enough
Appreciate any help
October 13th, 2010 - 15:17
I will take anything you have I got a guy that can work with C#
October 13th, 2010 - 15:19
Do you want to ping me an email to steve at goodman dot net, and I’ll fire back you a ZIP file.
October 13th, 2010 - 15:43
sent
October 15th, 2010 - 11:01
Hey,
the script work´s perfect for me but is there an solution to import the contact´s into a contact folder int he public store ?
Thx
October 15th, 2010 - 23:44
Hiya,
There’s nothing built in but you can use EWS with Public Folders, at least in the 2010 SP1 version AFAIK. I’ve not tried it myself though.
Steve
October 21st, 2010 - 17:22
Steve, Thanks for your help. got alomost all of it working except I get the following error when the ps script is run
Exception calling “Bind” with “2″ argument(s): “Request failed. The remote server returned an error: (401) Unauthorized
.”
October 21st, 2010 - 18:00
figured it out
it was not explicit on the domain
October 22nd, 2010 - 01:40
Cool, glad it’s working
November 30th, 2010 - 02:17
Thank you for this script! It will be really useful to me. Here are some points I encountered getting it to work with Live@Edu, which I thought other readers might find useful.
As the EWS API available for download now is version 1.1 rather than 1.0, if you install in the default path, you need to specify the EWS path to the script or amend the script.
Live@Edu Outlook Live is Outlook 2010. (This is stated in other posts by Steve but not clear from this one alone)
Autodiscovery of Live@Edu EWS did not work for me. I got an exception: “Autodiscover blocked a potentially insecure redirection to
https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml. To allow Autodiscover to follow the redirection, use the AutodiscoverUrl(string, AutodiscoverRedirectionUrlValidationCallback) overload.” To make it work I think it is necessary to pass a delegate that returns true, but I have not worked out how to define delegates in PowerShell. I avoided using autodiscovery by giving the EWS address: https://pod51007.outlook.com/EWS/Exchange.asmx. You can see your pod number from the URL when you view your mail.
As bigvin found, if I specified the domain argument to the script, I got an exception:
Exception calling “Bind” with “2″ argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”
It worked to have no domain argument, but “@ourdomain.org.uk” at the end of the admin’s username.
I also got this same 401 exception if I attempted to use a service account other than our original admin account, even if that account was assigned all the same admin roles, including ApplicationImpersonation. I would be very interested to know why this is.
December 1st, 2010 - 19:35
Hi Richard,
You’re right, EWS 1.1 RTM’ed in the meantime. I’ll update that. I’ve got a code snippet somewhere for that delegate so will dig it out and add it in..
I’ll also spend a bit more time testing against outlook live and make the experience better. May take a few days tho.. Need a marathon blog update day I think!
Steve
December 3rd, 2010 - 00:40
All that would be great. I will look out for the update.
No need to rush though: the post is fabulously useful already. I hope your update day is the modern kind of marathon where people dress up in ridiculous costumes and not the original type where you drop dead at the end!
Thanks again
Richard
December 4th, 2010 - 22:19
LOL, I think my blog update evenings are a bit of both
October 14th, 2011 - 20:53
I too am experiencing difficulty with a 401 exception when attempting to run this script. Any assistance would be greatly appreciated.
January 20th, 2011 - 17:51
Steve,
Thanks a ton for creating this script! With over 1000 users wanting their external contacts from their Apple address books upon migration to Exchange 2010 (OWA only), this is a real life saver.
Since the EWS was updated to 1.1 (as Richard mentioned), just had to adjust the script to read 1.1 instead of 1.0 where you reference it. Additionally, I too could not get the script to run with the -domain flag, but as stated above, the workaround was to put the full upn in instead.
Thanks again for your great work on this!
-David
January 23rd, 2011 - 00:06
Hi David,
Thanks for the reminder. I’ve updated the script and download link in the article
Steve
January 28th, 2011 - 20:11
Steve
Thank you for this script, but I have one question. Not being a coder, how do I direct the script to save contacts in a sub-folder of the main contacts folder for a user?
March 18th, 2011 - 17:29
Hi Steve,
I was one of the unlucky ones who actually downloaded and installed SP1 RU3 recently. I know Microsoft pulled this patch due to the BES issues however, this script now no longer works correctly. I run exactly the same script, it appears to add all of the contacts, but when you go to the person’s contact folder, no contacts are showing up!
I am not sure where to begin since the script “appears” to run just fine. I see the names being added and everything…but no contacts show up. I have tested this multiple ways so it’s not a the csv file or the mailbox. It’s like it places them into the mailbox hidden from view.
Any ideas on how to fix it?
March 19th, 2011 - 20:23
Fixed after uninstall of RU3.
March 19th, 2011 - 22:28
Hi David,
That’s rather worrying that it managed to get broken. I’ll be sure to test on Update Rollup 3 when it’s re-released and the beta for SP2.
Steve
April 8th, 2011 - 15:45
I’ve just been successful on 2010 SP1 RU3. No problems here – ran first time (after I change 1.0 to 1.1 twice in the script)
April 8th, 2011 - 16:59
Fantastic, that’s great news
March 31st, 2011 - 05:38
Hi
I am trying to run the script it gives me an error Parameter CSVFileName must be specified. I am new to powershell. I saved my contacts.csv file in the same directory where script is and run the script in Exchange powershell console.
Do I need to give the path or name of CSV file in script some where ?
Thanks
Worbis
May 25th, 2011 - 14:57
Is there a way to use the 2010 EWS without SSL/TLS Cerificate?
Trying to use the script but failing with:
Exception calling “Bind” with “2″ argument(s): “The request failed. The underlying connection was closed: Could not est
ablish trust relationship for the SSL/TLS secure channel.”
At C:\ContactTest\Import-MailboxContacts.ps1:172 char:81
May 31st, 2011 - 19:11
If you run this script several times against the same mailbox, it will create multiple copies of each contact in the .csv file. Can it be made to only update fields from the CSV file but leave everything else in the contact the same?
September 19th, 2011 - 15:52
I also think it would be very useful to update existing contacts instead of duplicating.
May 31st, 2011 - 19:18
Even better – if the contact is there, edit it – otherwise, create a new one.
Thanks so much!
PJ
June 4th, 2011 - 17:25
Steve,
Thanks this is a life saver. We are a small company with just 3 people and this is awsome!! Was wondering, if you have a script that can allow me to add external contacts through a csv file, so that these external contacts are available to all e-mail users in the company.
I am not a IT guy and would not know how to modify the script you have provided to update the contacts for each mailbox. Any help would be appreciated…..
Pinakin
June 4th, 2011 - 19:29
Steve,
Thanks this is a life saver. We are a small company with just 3 people and this is awsome!! Was wondering, if you have a script that can allow me to add external contacts through a csv file, so that these external contacts are available to all e-mail users in the company.
I am not a IT guy and would not know how to modify the script you have provided to update the contacts for each mailbox. Any help would be appreciated…..
PS: We just enrolled into the Office 365 program…… Plan E3 beta…….
Pinakin
June 28th, 2011 - 09:53
Thank you for this script… I was able to run it sucessfully with Exchage 2007, but having some issues while running against exchange 2010 servers. Getting below exeption message, any help will be highly appreciated.
————–
Exception calling “Bind” with “2″ argument(s): “The SMTP address has no mailbox associated with it.”
At D:\Import-MailboxContacts\Import-MailboxContacts.ps1:172 char:81
+ $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind <<<< ($service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Contacts);
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : DotNetMethodException
—–
July 18th, 2011 - 08:24
Steve,
Great script. You mentioned earlier about adding support for importing groups also. I have a customer with a lot of users who have been using personal groups in their previous mail system. The groups can be exported to CSV files. Is it possible to extend the script?
Regards,
Richard
September 6th, 2011 - 21:39
Hello,
Thank you for writing a script to do exactly what I need. As Richard Bunch and BigVin above him have stated about the newer EWS path, I updated the path in the script to be 1.1. I setup the impersonation for the original administrator account for the Live@edu domain that I was connecting to and this was accepted. I used the pod address for my connection as Richard suggested as autodiscovery did not work and also the -domain switch; I had to use my domain suffix for the user I was targeting. However I get the same error as Bigvin; I was adding contacts to the administrator account contacts, as the administrator.
If I use import-contactslist with a csv file then the contacts get imported, however they do not show up under the contact list, only the contacts folder.
import-contactlist -CSV -CSVData ([System.IO.File]::ReadAllBytes “D:\Live@edu\schoolname\contacts\contacts.csv”)) -Identity administrator@schooldomain.sch.uk
Below is the error that I get from your script:
Exception calling “Bind” with “2″ argument(s): “The request failed. The remote
server returned an error: (401) Unauthorized.”
At D:\Live@Edu\Scripts\Import-MailboxContacts.ps1:172 char:81
+ $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::B
ind <<<< ($service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::
Contacts);
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : DotNetMethodException
This is a connection to Live@edu from a Windows 7 SP1 Pro 32bit machine.
September 20th, 2011 - 22:57
Thank you! This script is fantastic and works perfectly, but I was asked to also pull in Categories as well. I added the colum to my CSV and added:
“CATEGORY” = “Item:Categories”
It doens’t seem to be importing them. Any idea of what I’m missing?
September 20th, 2011 - 23:09
Hiya,
Unfortunately, the script doesn’t have any code to add the categories (and I don’t think I tested this either). It may be that’s something that need to be set manually.
Steve
October 18th, 2011 - 08:52
Hi guys
how good I found here with a lot of expert persons , bcz I’m really beginner in exchange.
I’ve recently migrated from qmail to exchange, so as you mentioned contacts aren’t migrated.
I did exactly like what you wrote above but I got error (I used administrator user , however I have username and password for mailbox if it’s needed)
I wrote this command:
.\Import-MailboxContacts.ps1 -CSVFileName “C:\e.sadegh.csv” -EmailAddresse.sadegh@domain.com -Impersonate $true -Username xadmin -Password 123456 -Domain domain.com
and get this error:
Exception calling “AutodiscoverUrl” with “1″ argument(s): “The Autodiscover service couldn’t be located.”
At C:\Program Files\Microsoft\exchange server\V14\Scripts\Import-MailboxContacts.ps1:159 char:33
+ $service.AutodiscoverUrl <<<< ($EmailAddress);
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : DotNetMethodException
should I use "-EwsUrl https://172.25.25.20/EWS/Exchange.asmx" in command? I used but got error again …
October 23rd, 2011 - 00:09
What was the error you got with EwsURL? Do you have a name you can use with a valid SSL certificate for the Exchange Server?
Steve
November 11th, 2011 - 12:14
You need to use the direct link to EWS, as Steve noted, do this by using the switch Steve built in:
-EWSUrl https://servername.domain/EWS/Exchange.asmx
I have a very strange problem, I was using the script all was going well, I could import some example csv’s and was tuning the script (location, attributemappings etc). After a while I was no longer able to run the commands, I got the same error as people that were replying before:
Exception calling “Bind” with “2″ argument(s): “The request failed. The remote server returned an error: (401) Unauthor
ized.”
At C:\CSV-Import\Fullscript-v1\Import-MailboxContacts-FINAL.ps1:133 char:81
+ $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind <<<< ($service, [Microsoft.Exchange.
WebServices.Data.WellKnownFolderName]::Contacts);
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : DotNetMethodException
Anyone have an idea why this is happening?
November 14th, 2011 - 11:13
In addition to this, the fault mentioned above was caused by the following:
I did not use the following command upfront, in my case it was needed every time:
New-ManagementRoleAssignment -Name:ImpersonationAssigmentName -Role:ApplicationImpersonation -User:[emailaddressofimpersonateduser]
After that it worked again.
Thanks Steve for the perfect setup!
January 19th, 2012 - 13:43
And a last comment. The reply above can be ignored, this was not the problem.
When scripting these kinds of actions it appears the O365 server needs to catch it’s breath after each action. The things I found out:
- Make sure you are on a stable connection
- Put delays (code “Start-Sleep -Seconds 1″) between actions such as impersonating a user, binding the contacts folder, creating a folder, creating a contact etcetera.
After sticking to both of the above I saw no more 401′s
Best of luck to you all and thanks again Steve.
February 1st, 2012 - 18:21
Thanks, and cheers for sharing your experiences
Steve