Home » Articles » A Mass Contact Conversion Experience

A Mass Contact Conversion Experience

This may save you a fair bit of trouble in the future. I was working on a client with an Exchange 2007 server that was overrun with contacts. That’s not necessarily a problem in and of itself, but these contacts had served their purpose and now needed to be turned into mail enabled users. They already had a number of duplicates between AD users and contacts and it was causing a lot of trouble in their SharePoint hierarchy. Fortunately PowerShell can step in and save the day! Or would you really enjoy manually copying over all of the contact’s profile data to an AD user? Not my idea of fun.

Here’s the code. You’ll want to tweak it to your purposes of course. I wanted some clear on the screen results as it progressed and also have an HTML report that I can reference afterwards. This marked my first venture in LINQ with moderate success. I will have to revisit things later to get it working exactly how I want.

<#
.SYNOPSIS
Reads in a CSV of contacts or mail enabled users and moves the contacts to mail enabled users or updates
the current mail enabled users

.DESCRIPTION
Convert-CSVContactsToUsers reads in the CSV specified in the command line. This CSV contains a list
of users that either currently have a contact which needs to be converted to a mail enabled
user, have both a contact and a user that is not mail enabled which needs the info from the contact
copied over to the user which is then mail enabled, or have neither a contact or a user and need
to have a mail enabled user created for them.

.PARAMETER FilePath
The path and filename to the CSV containing the required users.

.PARAMETER DomainController
The NetBIOS or FQDN for the target DC to use.

.PARAMETER ResultsFile
The filename to save the HTML formatted results to.

.EXAMPLE
Convert-CSVContactstoUsers -FilePath .\UserList.csv -DomainController dc01 -ResultsFile .\Results.html

.NOTES
Require Server 2008 R2 and should be run from the EMS.
.NET 3.5 and above required for HTML output.
Appropriate domain and Exchange rights are required.

Revision History
v1.0 - Initial release
v1.1 - Added X500 address, moved status report
#>

[CmdletBinding()]
param(
[Parameter(Mandatory=$True)][string]$FilePath,
[Parameter(Mandatory=$True)][string]$DomainController,
[Parameter(Mandatory=$False)][string]$ResultsFile
)

Import-Module ActiveDirectory

#$ErrorActionPreference= 'silentlycontinue'
$CSVList = Import-CSV -Path "$($FilePath)"
$Password = ConvertTo-SecureString -String "expPassword!!" -AsPlainText -Force

# Create a custom table for storing the results
$ResultsTable = New-Object System.Data.DataTable "Conversion Results"
$column01 = New-Object System.Data.DataColumn User
$column02 = New-Object System.Data.DataColumn Result

$ResultsTable.columns.add($column01)
$ResultsTable.columns.add($column02)

# Loop through each CSV entry, check for object existence
# then process the object based on existence and type
foreach($TargetUser in $CSVList)
{
# Check for existence of any objects
$ADContact = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=contact))" -SearchScope Subtree -Properties * -Server $DomainController
$ADUser = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=user))" -SearchScope Subtree -Properties * -Server $DomainController
$ResultsRow = $ResultsTable.NewRow()
$ResultsRow.User = $TargetUser.Name
[string]$Status = $null

# If both contact and user exist, copy the info from the contact
# into the user's properties, remove the contact, then mail enable the user
if($ADContact -and $ADUser)
{
# First copy over any of the current profile details

if($ADContact.directReports)
{
$ADUser.directReports = $ADContact.directReports
}
if($ADContact.homePhone)
{
$ADUser.homePhone = $ADContact.homePhone
}
if($ADContact.facsimileTelephoneNumber)
{
$ADUser.facsimileTelephoneNumber = $ADContact.facsimileTelephoneNumber
}
if($ADContact.l)
{
$ADUser.l = $ADContact.l
}
if($ADContact.manager)
{
$ADUser.manager = $ADContact.manager
}
if($ADContact.mobile)
{
$ADUser.mobile = $ADContact.mobile
}
if($ADContact.physicalDeliveryOfficeName)
{
$ADUser.physicalDeliveryOfficeName = $ADContact.physicalDeliveryOfficeName
}
if($ADContact.postalCode)
{
$ADUser.postalCode = $ADContact.postalCode
}
if($ADContact.sn)
{
$ADUser.sn = $ADContact.sn
}
if($ADContact.st)
{
$ADUser.st = $ADContact.st
}
if($ADContact.streetAddress)
{
$ADUser.streetAddress = $ADContact.streetAddress
}
if($ADContact.telephoneNumber)
{
$ADUser.telephoneNumber = $ADContact.telephoneNumber
}
if($ADContact.title)
{
$ADUser.title = $ADContact.title
}
if($ADContact.department)
{
$ADUser.department = $ADContact.department
}
if($ADContact.Description)
{
$ADUser.Description = $ADContact.Description
}
if($ADContact.c)
{
$ADUser.c = $ADContact.c
}
if($ADContact.co)
{
$ADUser.co = $ADContact.co
}
if($ADContact.countryCode)
{
$ADUser.countryCode = $ADContact.countryCode
}
if($ADContact.info)
{
$ADUser.info = $ADContact.info
}
if($ADContact.initials)
{
$ADUser.initials = $ADContact.initials
}
if($ADContact.ipPhone)
{
$ADUser.ipPhone = $ADContact.ipPhone
}
if($ADContact.pager)
{
$ADUser.pager = $ADContact.pager
}
if($ADContact.wWWHomePage)
{
$ADUser.wWWHomePage = $ADContact.wWWHomePage
}
if($ADContact.postOfficeBox)
{
$ADUser.postOfficeBox = $ADContact.postOfficeBox
}
if($ADContact.company)
{
$ADUser.company = $ADContact.company
}

# Update the user with the current info
Set-ADObject -Instance $ADUser -Server $DomainController

# Loop through the groups and add the user to them
foreach($ADGroup in $ADContact.memberOf)
{
Add-ADGroupMember -Identity "$($ADGroup)" -Members $ADUser -Server $DomainController
}

# Next, remove the contact
Remove-MailContact -Identity $ADContact.DistinguishedName -Confirm:$false -DomainController $DomainController

# Enable the current user, then copy over the remaining attributes
Enable-MailUser -Identity $ADUser.DistinguishedName -ExternalEmailAddress $ADContact.mail -Alias $ADContact.mailNickname -DomainController $DomainController | Out-Null

# Add the X500 address if the contact was stamped with a legacyExchangeDN
if($ADContact.legacyExchangeDN)
{
$X500User = Get-MailUser -Identity $ADUser.DistinguishedName -DomainController $DomainController
$X500User.EmailAddresses += [Microsoft.Exchange.Data.CustomProxyAddress]("X500:$($ADContact.legacyExchangeDN)")

Set-MailUser -Identity $X500User -EmailAddresses $X500User.EmailAddresses -DomainController $DomainController
}

Write-Host "$($TargetUser.Name) converted."
$Status = "Success"
}
elseif($ADContact)
{
# First remove the contact
Remove-MailContact -Identity $ADContact.DistinguishedName -Confirm:$false -DomainController $DomainController

# Create the mail enabled user
New-MailUser -Name "$($TargetUser.Name)" -ExternalEmailAddress $ADContact.mail -Alias $ADContact.mailNickname -UserPrincipalName "$($ADContact.mailNickname)@domain.local" -Password $Password -ResetPasswordOnNextLogon:$true -DomainController $DomainController | Out-Null

# Then copy in the attributes
$ADUser = Get-ADObject -LdapFilter "(&(CN=$($TargetUser.Name))(ObjectClass=user))" -SearchScope Subtree -Properties * -Server $DomainController
$ADUser.directReports = $ADContact.directReports
$ADUser.homePhone = $ADContact.homePhone
$ADUser.facsimileTelephoneNumber = $ADContact.facsimileTelephoneNumber
$ADUser.l = $ADContact.l
$ADUser.manager = $ADContact.manager
$ADUser.mobile = $ADContact.mobile
$ADUser.physicalDeliveryOfficeName = $ADContact.physicalDeliveryOfficeName
$ADUser.postalCode = $ADContact.postalCode
$ADUser.sn = $ADContact.sn
$ADUser.st = $ADContact.st
$ADUser.streetAddress = $ADContact.streetAddress
$ADUser.telephoneNumber = $ADContact.telephoneNumber
$ADUser.title = $ADContact.title
$ADUser.department = $ADContact.department
$ADUser.Description = $ADContact.Description
$ADUser.c = $ADContact.c
$ADUser.co = $ADContact.co
$ADUser.countryCode = $ADContact.countryCode
$ADUser.info = $ADContact.info
$ADUser.initials = $ADContact.initials
$ADUser.ipPhone = $ADContact.ipPhone
$ADUser.pager = $ADContact.pager
$ADUser.wWWHomePage = $ADContact.wWWHomePage
$ADUser.postOfficeBox = $ADContact.postOfficeBox
$ADUser.company = $ADContact.company
# Copying over the X500 address if it exists
if($ADContact.legacyExchangeDN)
{
$X500User = Get-MailUser -Identity $ADUser.DistinguishedName -DomainController $DomainController
$X500User.EmailAddresses += [Microsoft.Exchange.Data.CustomProxyAddress]("X500:$($ADContact.legacyExchangeDN)")

Set-MailUser -Identity $X500User -EmailAddresses $X500User.EmailAddresses -DomainController $DomainController
}

Set-ADObject -Instance $ADUser -Server $DomainController

# Loop through the groups and add the user to them
foreach($ADGroup in $ADContact.memberOf)
{
Add-ADGroupMember -Identity "$($ADGroup)" -Members $ADUser -Server $DomainController
}

Write-Host -ForegroundColor Yellow "$($TargetUser.Name) created."
$Status = "Success"
}
elseif($ADUser)
{
# Only a user is found
Write-Host -ForegroundColor Cyan "$($TargetUser.Name) already exists."
$Status = "Exists"
}
else
{
Write-Host -ForegroundColor Magenta "$($TargetUser.Name) not found!"
$Status = "Failed"
}

# Update the results
$ResultsRow.Result = $Status

# Clear the variables to prevent false positives on the next loop
$ADContact = $null
$ADUser = $null
$Status = $null

# Update the results table
$ResultsTable.Rows.Add($ResultsRow)
}

# Check if HTML results should be written out
if($ResultsFile)
{
# Build the style sheet for the page
$Style = "<style>"
$Style = $Style + "body{font-family: `"Century Gothic`"; font-size: 10pt;}"
$Style = $Style + "table{border-width: 1px; border-style: solid; border-color black; border-collapse: collapse; }"
$Style = $Style + "th{border-width: 1px; border-style: solid; border-color: black; background-color: #CBFEFF; }"
$Style = $Style + "td{border-width: 1px; border-style: solid; border-color: black; text-align: center}"
$Style = $Style + "</style>"

# LINQ will be used for easier custom formatting
Add-Type -AssemblyName System.Xml.Linq

# Convert the desired columns into HTML and convert it to XML
$xml = [System.Xml.Linq.XDocument]::Parse("$($ResultsTable | Select User,Result | ConvertTo-Html -Head $Style)")

# Define the namespace
if($Namespace = $xml.Root.Attribute("xmlns").Value)
{
$Namespace = "{{{0}}}" -f $Namespace
}

#
# $xml.Descendants().Value is not returning the values for some reason here
# Have to resort to alternate index discovery
#
#$wsIndex = [Array]::IndexOf($xml.Descendants("${Namespace}th").Value, "Result")
[Array]$xmlArray = $xml.Descendants("${Namespace}th")
for($i=0;$i -le $xmlArray.length-1;$i++)
{
#We're color coding the Results column
if($xmlArray[$i] -match "Result")
{
$wsIndex = $i
}
}

# Loop through each row and assign a text color and background color to the result
foreach($row in $xml.Descendants("${Namespace}tr"))
{

switch(@($row.Descendants("${Namespace}td"))[$wsIndex])
{
{"Success" -eq $_.Value} {$_.SetAttributeValue("style", "color: #006600; background-color: #F1FEFE;"); continue}
{"Failed" -eq $_.Value} {$_.SetAttributeValue("style", "color: #660000; background-color: #F1FEFE;"); continue }
{"Exists" -eq $_.Value} {$_.SetAttributevalue("style", "color: #4D6600; background-color: #F1FEFE;"); continue }
}
}

# Save to the desired file
$xml.Save("$pwd/$($ResultsFile)")
}

Use safely!


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

wordpress visitor counter

RSS Subscriptions

Contact Me