I had a project at work where I needed to streamline uploading contacts from a public folder to a user’s personal contacts. I was able to find this article to get me started. It really helped figuring out how to take a CSV file and use the MS Graph API to import the data into a user profile.
I was coming from an environment where we used to have an On Prem / Hosted Exchange so the information I needed was already there, but it was in a format that was incompatible to the new M365 environment. There were some differences to account for.
The Differences in My Situation
My requirements were a bit different:
While the article shows you how to upload the CSV into the root of contacts, I needed to upload the data into a specific folder in contacts. The way we use contacts is the root is used for personal contacts and a folder below is used for staff contacts. It easily syncs to our Android phones. Sometimes we need to do a mass delete of staff contacts and this will not interfere with personal contacts.
The Sample contact list in the article is a very small sample. I needed to figure out all the contact fields MS Graph / M365 uses.
There was another contact list I needed to automate as well but it used a lot of fields that are only available in Office and not Microsoft 365. Office contact fields and M365 Contact fields don’t really link up. If you are NOT creating a contact list CSV from scratch (which most of you probably are doing), you must look at the field mappings that I will provide later in the article.
I also needed to upload staff photos. Unfortunately using “For Each” does not work (I will show you later) and went with the old standard “while” loop!
So now that I have told you what difference I was up against. I will show you how I made things work.
Create a Registered App in Azure AD
Before you can use MS Graph to automate a lot of the functionality in Outlook you need to create an app in Azure AD. I like to think of it as login credentials that your MS Graph / PowerShell uses to get access to the M365 environment.
By default, only Global Administrator, Cloud Application Administrator and Application Administrator roles have access. You will need to give permission for the app to do the following:
Contacts.ReadWrite application permission.
Also, make sure to grant admin consent for the permission to allow the script to access the contacts for all users.
You will need the following information for your script, the Application (client) ID and Directory (tenant) ID from the Overview page:
Lastly, open the Certificates & Secrets page and create a new client secret. Note the value of the secret. This combination of the App Identifier, Tenant identifier, and App secret will allow our script to authenticate and use the assigned permission to interact with user mailboxes.
Creating the CSV File
This where I had mentioned Earlier if you were taking an older Office Contact CSV and need to get it to work with M365 you will have problems. I will give you a list of the older Office fields and what they map to in M365. If they don’t, I will note it as such. You will have to use another field in M365 (if you can) to map to (This list is not exhaustive):
Office Contact Field
M365 Equivalent
File As
not manditory but is involved with title of contact in business card view (fileAS)
Display Name
must create or MS Graph will not create contact record(displayName)
Assistant’s Name
assistantName
Business Country/Region
countryOrRegion
Business Fax
No Equivalent
Web Page Address
businessHomePage
Business City
city
Business Fax
fax
Business Phone
businessPhone
Business Phone 2
businessPhone2
Business State
state
Business Street
street
Company
companyName
Department
department
E-mail Address
emailaddress
E-mail Display Name
emailname
E-mail 2 Address
emailaddress2
E-mail 2 Display Name
emailname2
E-mail 3 Address
emailaddress3
E-mail 3 Display Name
emailname3
First Name
givenName
Home Phone
homePhone
Home Phone 2
homePhone2
Initials
initials
Job Title
jobTitle
Last Name
surName
Manager’s Name
manager
Middle Name
middleName
Mobile Phone
mobilePhone
Notes
personalNotes
Other Fax
No Equivalent
Other Phone
No Equivalent
Radio Phone
No Equivalent
Title
No Equivalent that I know of
Zip / Postal code
postalCode
Contact Field Mappings
When you export your old contact file into a CSV, now you know what you will have to change the field names so the data will import properly using MS Graph. As mentioned above DisplayName must be populated, or the import won’t work.
One more VERY IMOPRTANT ITEM:
If using email addresses, the record can only have one or three email addresses in it. When you try to use only two or one in any other spot than the first field, the creation of the record will fail. Please ask Microsoft about this (LOL)?
Now that you have the format in which to create your CSV, let’s go on to the next step.
Creating the Scripts
To upload to the user’s contacts (in this case a contact folder), you need to create 3 scripts. One calls the other when it is done:
A script that declares the App Registration parameters and passes them to the subsequent scripts.
Another script that imports the contact info from a CSV file you specify.
Lastly, a Script that uploads contact photos from a folder you specify.
Declare App Registration Parameters
All this script is doing is taking the parameters you set in your Azure AD App registration (Client Secret, Client ID, and Tenant ID) and loading them into variables. It also loads the mailbox you wish to upload the contacts to and the path to the CSV file for good measure. Lastly, it calls the import contact PowerShell script.
Import the Contact Info From CSV
This is where the fun begins! This script has several functions which it calls in order
It imports the CSV file.
Checks to see if the contact folder already exists.
If it does it deletes it and recreates for import. If not, it creates for import.
For each contact record in the CSV file, it creates a contact record in the users Outlook Contacts
To end this script, it calls the upload photo script.
Upload Photos from a Folder
This script uses the same CSV file, and it matches each First and Last name with the naming convention of contact photos you have stored somewhere (Firstname Lastname.jpg) and uploads them to the user’s corresponding contact record.
The key takeaway here is to make sure all contact photos are named exactly as the first and last name of the person they belong to.
Another feature of the script is scrubs names with apostrophes. If single quotes are misinterpreted in PowerShell, things break. Make sure the corresponding photo is also named without the apostrophe.
I mentioned earlier in the article that “for each” method does not work when going through each record and uploading a photo. That is OK though. A while loop does the trick. After you get a count of the records in the CSV, it is a matter of going though each record an uploading the photo.
Advice on Troubleshooting
As with all scripting, you need to test, test and test some more before it ends up in production. Start with one record and get it to work. If the record doesn’t work, you may need to go field by field to narrow it down.
As an example, the personal notes field is a text field but cannot start with a “+” sign. It breaks the import somehow (I found this out the hard way).
Another piece of advice is to make sure you DO NOT name your field headings the same as the contact fields you are importing. For example, the contact field name for a person’s email display name is “emailname”. In your CSV, do not call it that. Call it something different like “emaildisplay”.
As well, if you are unsure on syntax and field format, you can always connect with Graph Explorer. You can easily connect to you own Outlook and run commands manually to see how they are supposed to look like or to test your syntax before you put it in the script.
I found it quite helpful.
Download the Example Files
But before yo do consider tipping me. It was some work to generate this script and it would help a long wat to keeping the site going….
I have the files located here: (Download Sample Script). You can download them and customize for your environment.
Final Thoughts
There are probably a lot of organizations that have been using Outlook for several years. More than likely, you are coming from either an On Prem or Hosted Exchange environment. Trying to get your contact information into M365 takes a little bit of work, but it can be done. It is automation at its finest!
I hope this information will help you on your journey.
If you find this post useful please consider buying me a coffee. It will keep me caffeinated so I can provide more Quick IT Tips!
I am an IT professional with over twenty years experience in the field. I have supported thousands of users over the years. The organizations I have worked for range in size from one person to hundreds of people. I have performed support from Help Desk, Network / Cloud Administration, Network Support, Application Support, Implementation and Security.
{"id":"10","mode":"form","open_style":"in_place","currency_code":"CAD","currency_symbol":"$","currency_type":"decimal","blank_flag_url":"https:\/\/www.cayville.ca\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/blank.gif","flag_sprite_url":"https:\/\/www.cayville.ca\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/flags.png","default_amount":500,"top_media_type":"none","featured_image_url":false,"featured_embed":"","header_media":null,"file_download_attachment_data":null,"recurring_options_enabled":true,"recurring_options":{"never":{"selected":true,"after_output":"One time only"},"weekly":{"selected":false,"after_output":"Every week"},"monthly":{"selected":false,"after_output":"Every month"},"yearly":{"selected":false,"after_output":"Every year"}},"strings":{"current_user_email":"","current_user_name":"","link_text":"Leave a tip","complete_payment_button_error_text":"Check info and try again","payment_verb":"Tip","payment_request_label":"Quick M365 Tips","form_has_an_error":"Please check and fix the errors above","general_server_error":"Something isn't working right at the moment. Please try again.","form_title":"Quick M365 Tips....Tips!","form_subtitle":"If you find my M365 tips helpful show your appreciation!! Thank you!!","currency_search_text":"Country or Currency here","other_payment_option":"Other payment option","manage_payments_button_text":"Manage your payments","thank_you_message":"Thank you for being a supporter!","payment_confirmation_title":"Quick M365 Tips","receipt_title":"Your Receipt","print_receipt":"Print Receipt","email_receipt":"Email Receipt","email_receipt_sending":"Sending receipt...","email_receipt_success":"Email receipt successfully sent","email_receipt_failed":"Email receipt failed to send. Please try again.","receipt_payee":"Paid to","receipt_statement_descriptor":"This will show up on your statement as","receipt_date":"Date","receipt_transaction_id":"Transaction ID","receipt_transaction_amount":"Amount","refund_payer":"Refund from","login":"Log in to manage your payments","manage_payments":"Manage Payments","transactions_title":"Your Transactions","transaction_title":"Transaction Receipt","transaction_period":"Plan Period","arrangements_title":"Your Plans","arrangement_title":"Manage Plan","arrangement_details":"Plan Details","arrangement_id_title":"Plan ID","arrangement_payment_method_title":"Payment Method","arrangement_amount_title":"Plan Amount","arrangement_renewal_title":"Next renewal date","arrangement_action_cancel":"Cancel Plan","arrangement_action_cant_cancel":"Cancelling is currently not available.","arrangement_action_cancel_double":"Are you sure you'd like to cancel?","arrangement_cancelling":"Cancelling Plan...","arrangement_cancelled":"Plan Cancelled","arrangement_failed_to_cancel":"Failed to cancel plan","back_to_plans":"\u2190 Back to Plans","update_payment_method_verb":"Update","sca_auth_description":"Your have a pending renewal payment which requires authorization.","sca_auth_verb":"Authorize renewal payment","sca_authing_verb":"Authorizing payment","sca_authed_verb":"Payment successfully authorized!","sca_auth_failed":"Unable to authorize! Please try again.","login_button_text":"Log in","login_form_has_an_error":"Please check and fix the errors above","uppercase_search":"Search","lowercase_search":"search","uppercase_page":"Page","lowercase_page":"page","uppercase_items":"Items","lowercase_items":"items","uppercase_per":"Per","lowercase_per":"per","uppercase_of":"Of","lowercase_of":"of","back":"Back to plans","zip_code_placeholder":"Zip\/Postal Code","download_file_button_text":"Download File","input_field_instructions":{"tip_amount":{"placeholder_text":"How much would you like to tip?","initial":{"instruction_type":"normal","instruction_message":"How much would you like to tip? Choose any currency."},"empty":{"instruction_type":"error","instruction_message":"How much would you like to tip? Choose any currency."},"invalid_curency":{"instruction_type":"error","instruction_message":"Please choose a valid currency."}},"recurring":{"placeholder_text":"Recurring","initial":{"instruction_type":"normal","instruction_message":"How often would you like to give this?"},"success":{"instruction_type":"success","instruction_message":"How often would you like to give this?"},"empty":{"instruction_type":"error","instruction_message":"How often would you like to give this?"}},"name":{"placeholder_text":"Name on Credit Card","initial":{"instruction_type":"normal","instruction_message":"Enter the name on your card."},"success":{"instruction_type":"success","instruction_message":"Enter the name on your card."},"empty":{"instruction_type":"error","instruction_message":"Please enter the name on your card."}},"privacy_policy":{"terms_title":"Terms and conditions","terms_body":null,"terms_show_text":"View Terms","terms_hide_text":"Hide Terms","initial":{"instruction_type":"normal","instruction_message":"I agree to the terms."},"unchecked":{"instruction_type":"error","instruction_message":"Please agree to the terms."},"checked":{"instruction_type":"success","instruction_message":"I agree to the terms."}},"email":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email address"},"success":{"instruction_type":"success","instruction_message":"Enter your email address"},"blank":{"instruction_type":"error","instruction_message":"Enter your email address"},"not_an_email_address":{"instruction_type":"error","instruction_message":"Make sure you have entered a valid email address"}},"note_with_tip":{"placeholder_text":"Your note here...","initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"empty":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"not_empty_initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"saving":{"instruction_type":"normal","instruction_message":"Saving note..."},"success":{"instruction_type":"success","instruction_message":"Note successfully saved!"},"error":{"instruction_type":"error","instruction_message":"Unable to save note note at this time. Please try again."}},"email_for_login_code":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email to log in."},"success":{"instruction_type":"success","instruction_message":"Enter your email to log in."},"blank":{"instruction_type":"error","instruction_message":"Enter your email to log in."},"empty":{"instruction_type":"error","instruction_message":"Enter your email to log in."}},"login_code":{"initial":{"instruction_type":"normal","instruction_message":"Check your email and enter the login code."},"success":{"instruction_type":"success","instruction_message":"Check your email and enter the login code."},"blank":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."},"empty":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."}},"stripe_all_in_one":{"initial":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"empty":{"instruction_type":"error","instruction_message":"Enter your credit card details here."},"success":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"invalid_number":{"instruction_type":"error","instruction_message":"The card number is not a valid credit card number."},"invalid_expiry_month":{"instruction_type":"error","instruction_message":"The card's expiration month is invalid."},"invalid_expiry_year":{"instruction_type":"error","instruction_message":"The card's expiration year is invalid."},"invalid_cvc":{"instruction_type":"error","instruction_message":"The card's security code is invalid."},"incorrect_number":{"instruction_type":"error","instruction_message":"The card number is incorrect."},"incomplete_number":{"instruction_type":"error","instruction_message":"The card number is incomplete."},"incomplete_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incomplete."},"incomplete_expiry":{"instruction_type":"error","instruction_message":"The card's expiration date is incomplete."},"incomplete_zip":{"instruction_type":"error","instruction_message":"The card's zip code is incomplete."},"expired_card":{"instruction_type":"error","instruction_message":"The card has expired."},"incorrect_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incorrect."},"incorrect_zip":{"instruction_type":"error","instruction_message":"The card's zip code failed validation."},"invalid_expiry_year_past":{"instruction_type":"error","instruction_message":"The card's expiration year is in the past"},"card_declined":{"instruction_type":"error","instruction_message":"The card was declined."},"missing":{"instruction_type":"error","instruction_message":"There is no card on a customer that is being charged."},"processing_error":{"instruction_type":"error","instruction_message":"An error occurred while processing the card."},"invalid_request_error":{"instruction_type":"error","instruction_message":"Unable to process this payment, please try again or use alternative method."},"invalid_sofort_country":{"instruction_type":"error","instruction_message":"The billing country is not accepted by SOFORT. Please try another country."}}}},"fetched_oembed_html":false}