Importing contacts into Exchange user mailbox

This is the second part of the original post Exporting Outlook contacts with PowerShell. I will be going through the process of importing these exported contacts directly into Exchange user mailboxes, in this case we will be using Exchange 2013. If you are using an older or newer version of Exchange server, you will need to use the relevant version of the EWS API, also you will need to adjust the dll path that exists in the PowerShell script supplied.

 

The brief steps to complete are as follows.

Install EWS API 2.1

Assign Role ApplicationImpersonation to Account used to complete this procedure

Modify and Save the ContactImport.ps1 script with your Exchange CAS server, Impersonation Account + Credentials and CSV share

Save the Import-MailboxContacts.ps1 script to the location specified in ContactImport.ps1

Open an Exchange Management Shell  and run ContactImport.ps1 script to import 

Else create session to Exchange CAS with the below and run ContactImport.ps1 changing EXCHANGESERVER to your Exchange CAS

$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://EXCHANGESERVER/powershell/ -Authentication kerberos -AllowRedirection
import-PSSession $session -AllowClobber

 

Prerequisites to this procedure include;

EWS API 2.1  – Install –> https://www.microsoft.com/en-us/download/details.aspx?id=42022 (Enables enhanced exchange management for third party applications)

CSV Files – If the CSV files have been created per user with the post Exporting Outlook contacts with PowerShell, the below script already includes all possible mapping for contacts properties, if a custom CSV file has been created then these mapping will need to be modified.

Exchange Impersonation Rights (Allows impersonation of users to enable the ability to import the contacts directly into mailboxes without the users credentials or full access rights to mailbox) See below –>

To configure impersonation rights, you will need to complete through either the Exchange Control Panel or Exchange Management Shell.

The steps to configure impersonation rights through ECP:

Access the ECP URL where EXCHANGESERVER is the name of your CAS and login with an administrative account e.g Exchange Domain Admins–> https://EXCHANGESERVER/ecp/

Select permissions –> admin roles –>

Permissions

Enter a relevant name e.g Impersonation –> Leave scope as Default –> Add Role ApplicationImpersonation –> Add the user in which you will use to complete the import under Member –> Click Save.

RoleGroup

Steps to configure impersonation through PowerShell:

Open Exchange Management Shell with an administrative account

New-ManagementRoleAssignment -Name ImpersonationRole -Role ApplicationImpersonation -User DOMAIN\ACCOUNTNAME

Now that impersonation is configured we can look to start the import process. In this specific use case the name of the CSV files are the name of the user account in the new domain, if you have a case where the new mailbox names differ from the CSV generated name, you will either need to change the generated name of the CSV or create a mapping between the CSV name and the new user account name.

 

 

Breakdown of the ContactImport script

Get all CSV file names from share and store in $list variable (Change SERVER\SHARE as appropriate)

$list = Get-childitem -Path "\\SERVER\SHARE\" | Where {$_.name -like "*.csv*"}

Loop through each CSV name in Share, If the name matches the UserPrincipalName property of the mailbox then import to users mailbox else display “No Address Found”

The ForEach uses the Import-MailboxContacts script which will be explained later in the post with the relevant parameters for EWS. You will need to change the EXCHANGESERVER name to your Exchange CAS server with the user name used earlier which has the impersonation rights.

ForEach ($Name in $list) {
    $filename = $name.FullName
    write-host $filename 
        
    IF ($email = Get-Mailbox | Where-Object {$_.UserPrincipalName -match $Name.basename}){
        write-host $email.PrimarySMTPAddress
        $CSV = $name.FullName
        C:\Scripts\Import-MailboxContacts.ps1 -CSVFileName "$filename" -EmailAddress $email.PrimarySMTPAddress -EwsUrl https://EXCHANGESERVER/EWS/Exchange.asmx -Username ACCOUNTNAME@DOMAINNAME -Impersonate $true -Password PASSWORD
        }ELSE {
            Write-Host "No Address Found!"       
        }
 }

 

 

Import-MailboxContacts script

At the bottom of this post is the Import-MailboxContacts script, thanks to Steve Goodman and has been configured to be used with the Exporting Outlook contacts with Powershell post.

The script needs to be saved as Import-MailboxContacts.ps1 and is called by the ContactImport script. The ContactMappings array has been modified to work with the export from Outlook and the script has been updated with the corrects paths for use with EWS 2.1.

And thats it, if all is configured correctly, your users should have newly imported contacts in their mailbox.

Hope this helps!

Full Scripts with comments below;

 

Import-MailboxContacts script

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.
# 	17/12/15 Edited by Chris Prevel to run on Exchange 2013, using EWS 2.1 API
# 	Added settings to run through proxy, changed mappings to match powershell export of contacts, changed dll path, updated attributes
#
# 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 "FirstName" is specified at line 102 as a required attribute and
# "FirstName" and "LastName" are hard coded at lines 187-197 when constructing NickName and FileAs.
$ContactMapping=@{
    "FirstName" = "GivenName";
    "MiddleName" = "MiddleName";
    "LastName" = "Surname";
    "CompanyName" = "CompanyName";
    "Department" = "Department";
    "JobTitle" = "JobTitle";
    "BusinessAddressStreet" = "Address:Business:Street";
    "BusinessAddressCity" = "Address:Business:City";
    "BusinessAddressState" = "Address:Business:State";
    "BusinessAddressPostalCode" = "Address:Business:PostalCode";
    "BusinessAddressCountry" = "Address:Business:CountryOrRegion";
    "HomeAddressStreet" = "Address:Home:Street";
    "HomeAddressCity" = "Address:Home:City";
    "HomeAddressState" = "Address:Home:State";
    "HomeAddressPostalCode" = "Other:Home:PostalCode";
    "HomeAddressCountry" = "Address:Home:CountryOrRegion";
    "OtherAddressStreet" = "Address:Other:Street";
    "OtherAddressCity" = "Address:Other:City";
    "OtherAddressState" = "Address:Other:State";
    "OtherAddressPostalCode" = "Address:Other:PostalCode";
    "OtherAddressCountry" = "Address:Other:CountryOrRegion";
    "AssistantTelephoneNumber" = "Phone:AssistantPhone";
    "BusinessFaxNumber" = "Phone:BusinessFax";
    "BusinessTelephoneNumber" = "Phone:BusinessPhone";
    "Business2TelephoneNumber" = "Phone:BusinessPhone2";
    "CallbackTelephoneNumber" = "Phone:CallBack";
    "CarTelephoneNumber" = "Phone:CarPhone";
    "CompanyMainTelephoneNumber" = "Phone:CompanyMainPhone";
    "HomeFaxNumber" = "Phone:HomeFax";
    "HomeTelephoneNumber" = "Phone:HomePhone";
    "Home2TelephoneNumber" = "Phone:HomePhone2";
    "ISDNNumber" = "Phone:ISDN";
    "MobileTelephoneNumber" = "Phone:MobilePhone";
    "OtherFaxNumber" = "Phone:OtherFax";
    "OtherTelephone" = "Phone:OtherTelephone";
    "PagerNumber" = "Phone:Pager";
    "PrimaryTelephoneNumber" = "Phone:PrimaryPhone";
    "RadioTelephoneNumber" = "Phone:RadioPhone";
    "TTYTDDTelephoneNumber" = "Phone:TtyTddPhone";
    "TelexNumber" = "Phone:Telex";
    "Anniversary" = "WeddingAnniversary";
    "Birthday" = "Birthday";
    "Email1Address" = "Email:EmailAddress1";
    "Email2Address" = "Email:EmailAddress2";
    "Email3Address" = "Email:EmailAddress3";
    "Initials" = "Initials";
    "OfficeLocation" = "OfficeLocation";
    "ManagerName" = "Manager";
    "Body" = "Body";
    "Profession" = "Profession";
    "Spouse" = "SpouseName";
    "WebPage" = "BusinessHomePage";
    "Contact Picture File" = "Method:SetContactPicture"
}
[System.Net.WebRequest]::DefaultWebProxy = $null

# 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]."FirstName")
    {
        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 (x86)\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
}
if (!(Get-Item -Path $EWSManagedApiDLLFilePath -ErrorAction SilentlyContinue))
{
    throw "EWS Managed API not found at $($EWSManagedApiDLLFilePath). Download from http://www.microsoft.com/download/en/details.aspx?id=28952";
}

# Load EWS Managed API
[void][Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Microsoft\Exchange\Web Services\2.1\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."FirstName" -and $ContactItem."LastName")
    {
        $ExchangeContact.NickName = $ContactItem."FirstName" + " " + $ContactItem."LastName";
    }
    elseif ($ContactItem."FirstName" -and !$ContactItem."LastName")
    {
        $ExchangeContact.NickName = $ContactItem."FirstName";
    }
    elseif (!$ContactItem."FirstName" -and $ContactItem."LastName")
    {
        $ExchangeContact.NickName = $ContactItem."LastName";
    }
    $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;
}

 

 

 

ContactImport Script

#Get all items of share with .csv file type
$list = Get-childitem -Path "\\SERVER\SHARE\" | Where {$_.name -like "*.csv*"}

#Loop through each .csv file in $list and if a mailbox 
#with the same User Principal Name exists then complete import
ForEach ($Name in $list) {
    $filename = $name.FullName
    write-host $filename 
        
    IF ($email = Get-Mailbox | Where-Object {$_.UserPrincipalName -match $Name.basename}){
        write-host $email.PrimarySMTPAddress
        $CSV = $name.FullName
        C:\Scripts\Import-MailboxContacts.ps1 -CSVFileName "$filename" -EmailAddress $email.PrimarySMTPAddress -EwsUrl https://EXCHANGESERVER/EWS/Exchange.asmx -Username ACCOUNTNAME@DOMAINNAME -Impersonate $true -Password PASSWORD
            }
        ELSE {
            Write-Host "No Address Found!"
          
    }
 }


 

Leave a Reply

Your email address will not be published. Required fields are marked *