-
Notifications
You must be signed in to change notification settings - Fork 0
Sync ContactsFromCsv
Sync-ContactsFromCsv.ps1 synchronizes contact information from a CSV file to users' contact folders in Microsoft Graph. This script is ideal for organizations that need to maintain consistent contact information across multiple mailboxes, such as shared contact directories, sales teams, or department-wide contact lists.
The script retrieves target users from a security group, CSV file, or direct list, then syncs contact data including phone numbers, names, company information, and job titles from a canonical CSV source.
- Security Group Integration - Automatically retrieve users from Entra security groups
- Flexible User Selection - Pull users from security groups, CSV files, or direct parameter lists
- Phone Number Normalization - Automatically strips formatting while preserving digits and international prefixes
-
Selective Updates - Update only phone numbers by default, or include names/company/job titles with
-UpdateNames -
Contact Cleanup - Optionally delete contacts not present in the source CSV with
-DeleteNotInCsv - Parallel Processing - Process multiple users concurrently with configurable thread count (1-16)
- Comprehensive Logging - Color-coded console output with detailed status messages and execution statistics
-
WhatIf Support - Preview changes before executing with
-WhatIfparameter - Automatic Folder Creation - Creates missing contact folders as needed
- PowerShell 7.2 or later (required by #Requires directive)
-
Microsoft.Graph.Authentication- Authentication to Microsoft Graph -
Microsoft.Graph.Users- User and contact management -
Microsoft.Graph.Groups- Security group membership retrieval
Will be automatically installed if missing during script execution.
The script requires Microsoft Graph permissions:
-
Contacts.ReadWrite- Create, update, and delete contacts -
User.Read.All- Retrieve user information -
Group.Read.All- Read security group membership (when using-SecurityGroup)
- Active internet connection
- Access to Microsoft Graph API
- Appropriate tenant permissions for contact folder operations
Type: String
Required: Yes
Description: Path to the CSV file containing canonical contact information to sync.
Expected CSV Columns:
-
Email(required) - Primary email address; used as unique identifier -
GivenName- First name (required for display name; updated with-UpdateNames) -
Surname- Last name (required for display name; updated with-UpdateNames) -
Company- Company name (updated with-UpdateNames) -
JobTitle- Job title or position (updated with-UpdateNames) -
BusinessPhones- Semicolon-separated list of business phone numbers -
MobilePhone- Mobile/cell phone number
See ContactList_Template.csv for example format.
Type: String
Required: Yes
Description: Name of the contact folder to sync. The folder will be created in each user's mailbox if it doesn't already exist.
Example: "Contacts", "Shared Contacts", "Sales Directory"
Type: String
Required: Yes (Parameter Set: SecurityGroup)
Description: Distinguished name (DN), display name, or object ID of the Entra security group whose members should be synced.
Formats Accepted:
- Display Name:
"Sales Team" - Distinguished Name:
"CN=Sales Team,OU=Groups,DC=contoso,DC=com" - Object ID:
"12345678-1234-1234-1234-123456789012"
Note: Mutually exclusive with -Users and -UsersCsvPath
Type: String Array
Required: Yes (Parameter Set: UsersList)
Description: Array of user principal names (UPNs) to sync.
Example:
-Users @("john.smith@contoso.com", "jane.doe@contoso.com")Note: Mutually exclusive with -SecurityGroup and -UsersCsvPath
Type: String
Required: Yes (Parameter Set: UsersCsv)
Description: Path to CSV file containing target users. CSV must have a UPN column with user principal names.
Example CSV format:
UPN
john.smith@contoso.com
jane.doe@contoso.com
robert.johnson@contoso.com
Note: Mutually exclusive with -SecurityGroup and -Users
Type: Switch
Required: No
Default: Not specified (disabled)
Description: If specified, deletes any contacts in the target contact folder that are not present in the source CSV.
Use Case: Ensure the contact folder matches the CSV exactly, removing outdated contacts
Warning: Use with caution - this will permanently remove contacts not in your CSV
Type: Switch
Required: No
Default: Not specified (disabled)
Description: If specified, updates contact names, company, and job titles in addition to phone numbers. By default, only phone numbers are synced.
Default Behavior (without flag): Only phone numbers are updated
With Flag: Updates GivenName, Surname, Company, and JobTitle
Type: Integer
Required: No
Default: 4
Valid Range: 1-16
Description: Number of parallel threads used to process users concurrently.
Recommendations:
- 1-2: For testing or low-memory environments
- 4 (default): Balanced for most scenarios
- 8-16: For large user counts (100+) on high-performance systems
Sync phone numbers for all members of a security group to their "Contacts" folder:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Contacts" `
-SecurityGroup "Sales Team"Include name and company information updates:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Contacts" `
-SecurityGroup "Sales Team" `
-UpdateNamesUpdate everything and delete contacts not in CSV:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Shared Contacts" `
-SecurityGroup "CN=Sales Team,OU=Groups,DC=contoso,DC=com" `
-UpdateNames `
-DeleteNotInCsvPreview what changes would be made without executing:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Contacts" `
-SecurityGroup "Sales Team" `
-WhatIfLoad target users from a CSV file:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Contacts" `
-UsersCsvPath "C:\target_users.csv"Sync specific users without a security group:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Sales Directory" `
-Users @("john.smith@contoso.com", "jane.doe@contoso.com", "bob.jones@contoso.com")Process 50+ users with 8 concurrent threads:
.\Sync-ContactsFromCsv.ps1 `
-CsvPath "C:\contacts.csv" `
-FolderName "Contacts" `
-SecurityGroup "Sales Team" `
-DegreeOfParallelism 8The script provides real-time color-coded feedback:
βΉοΈ [2026-03-12 14:23:45] Verifying required PowerShell modules...
β
[2026-03-12 14:23:46] Successfully connected to Microsoft Graph
βΉοΈ [2026-03-12 14:23:47] Loading canonical contacts from: C:\contacts.csv
β
[2026-03-12 14:23:47] Loaded 50 canonical contacts
βΉοΈ [2026-03-12 14:23:48] Retrieving members from security group: Sales Team
β
[2026-03-12 14:23:49] Found 25 members in security group
βΉοΈ [2026-03-12 14:23:50] Processing user: john.smith@contoso.com
β
[2026-03-12 14:23:51] Created contact: John Smith [john.smith@contoso.com]
...
After completion, displays execution statistics:
==================== SYNC COMPLETE ====================
Users Processed: 25
Total Contacts Created: 78
Total Contacts Updated: 145
Total Contacts Deleted: 12
======================================================
- β Green (Success) - Operation completed successfully
- β Red (Error) - Operation failed; check details
β οΈ Yellow (Warning) - Potential issue; operation may be skipped- βΉοΈ Cyan (Info) - Informational message; normal operation flow
| Column | Type | Notes |
|---|---|---|
| String | Unique identifier; no duplicates |
| Column | Type | Notes |
|---|---|---|
| GivenName | String | First name; updated with -UpdateNames
|
| Surname | String | Last name; updated with -UpdateNames
|
| Company | String | Organization; updated with -UpdateNames
|
| JobTitle | String | Position; updated with -UpdateNames
|
| BusinessPhones | String | Semicolon-separated; formatting auto-stripped |
| MobilePhone | String | Only first number used if multiple |
- Auto-Stripped Characters: Parentheses, hyphens, spaces, periods, commas
- Preserved: Digits (0-9) and plus sign (+)
-
International: Include + prefix (e.g.,
+1-555-123-4567β+15551234567) -
Multiple Business Phones: Separate with semicolon (e.g.,
(555) 123-4567; (555) 987-1234) - Display in CSV: Any formatting is acceptable; normalization is automatic
- Recommended: UTF-8 without BOM
- Fields with commas should be wrapped in quotes
- Both Unix (LF) and Windows (CRLF) line endings are supported
Symptoms: Error message: "Security group 'Sales Team' not found"
Causes:
- Security group doesn't exist or has been deleted
- User doesn't have permission to read the security group
- Group display name doesn't match exact string
Solutions:
- Verify security group exists in Entra:
Get-MgGroup -Filter "displayName eq 'Sales Team'"
- Use the group's object ID instead of display name
- Check user permissions in your tenant
- Ensure exact spelling and case sensitivity where applicable
Symptoms: Errors about missing Microsoft.Graph modules
Causes:
- Modules haven't been installed
- User lacks permission to install modules
- PowerShell doesn't have internet access
Solutions:
- Install modules manually:
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force Install-Module Microsoft.Graph.Users -Scope CurrentUser -Force Install-Module Microsoft.Graph.Groups -Scope CurrentUser -Force
- Run PowerShell as administrator if installing system-wide
- Check internet connectivity and proxy settings
Symptoms: Authentication error or connection timeout
Causes:
- Not logged into Azure/Microsoft 365
- MFA required but not available
- Network connectivity issue
Solutions:
- Verify you're connected to a network with Microsoft Graph access
- Ensure proper MFA setup if required
- Clear cached credentials:
Disconnect-MgGraph Connect-MgGraph -Scopes 'Contacts.ReadWrite', 'User.Read.All', 'Group.Read.All'
- Test connectivity:
Test-NetConnection graph.microsoft.com -Port 443
Symptoms: Script completes but shows zero changes
Causes:
- Email addresses in CSV don't match existing contacts
- Contacts folder permissions issue
- Phone numbers are already normalized correctly
Solutions:
- Verify email addresses match exactly:
Get-MgUserContactFolderContact -UserId "user@contoso.com" -All | Select DisplayName, EmailAddresses
- Check contact folder permissions
- Use
-WhatIfto preview what would happen:.\Sync-ContactsFromCsv.ps1 -CsvPath "..." -FolderName "..." -SecurityGroup "..." -WhatIf
Symptoms: Error when using -DeleteNotInCsv
Causes:
- Insufficient permissions to modify contacts
- Contact is protected or read-only
- Permission scope doesn't include Contacts.ReadWrite
Solutions:
- Verify user has Contacts.ReadWrite permission
- Remove
-DeleteNotInCsvflag and manually delete outdated contacts - Check if contacts are protected in organization policies
Symptoms: Script exits with file not found error
Causes:
- Incorrect file path
- File path contains spaces and isn't quoted
- File doesn't have .csv extension or has wrong format
Solutions:
- Use full path with quotes:
-CsvPath "C:\Reports\My Contacts.csv"
- Verify file exists:
Test-Path "C:\Reports\contacts.csv"
- Check CSV format includes Email column with data
- 1-10 users: DegreeOfParallelism = 2-4 (default fine)
- 11-50 users: DegreeOfParallelism = 4-6
- 50+ users: DegreeOfParallelism = 8-16
- 100+ users: DegreeOfParallelism = 12-16 (monitor system resources)
- Small contact folders (10-50 contacts): <1 second per user
- Medium contact folders (50-200 contacts): 2-5 seconds per user
- Large contact folders (200+ contacts): 5-15 seconds per user
Phone number updates use minimal bandwidth; name updates require slightly more.
- Test with
-WhatIfon a small subset first - Run during off-peak hours for production scenarios
- Monitor memory usage with high parallelism on large user counts
- Consider breaking large syncs into smaller groups if needed
- New-Office365Accounts - Create bulk user accounts (wiki page coming soon)
- Set-OfficeContacts - Manage individual contact properties (wiki page coming soon)
- Get-GraphToken.ps1 - Generate Microsoft Graph authentication tokens
-
v1.0 (2026-03-12) - Initial public release
- Security group member retrieval
- CSV-based contact syncing
- Parallel processing support
- Phone number normalization
- Contact cleanup with deletion support
- Comprehensive error handling and logging
- Overview
- Start-LyncCsvExporter
- Get-ComprehensiveLyncReport
- Get-LyncHealthReport
- Get-LyncInfrastructureReport
- Get-LyncServiceStatus
- Get-LyncUserRegistrationReport
- Export-ADLyncTeamsMigrationData
- New-Office365Accounts
- Sync-ContactsFromCsv
- Set-EmailToSharedAccount
- Set-SMTPForward
- Invoke-UserSignOutAndBlock
- Security Assessment Scripts (coming soon)
- Azure Automation (documentation pending)
- Get-GraphToken
- Get-GraphHeaders
- Get-AzureResourcePaging
- Get-EnterpriseAppUsage
- Get-ExchangeErrorsGraph
- Get-PBIWorkspaceUsageReport
- Intune Management (documentation pending)