User Profiles now (SharePoint 2010) is a SharePoint service application that use a Microsoft Forefront Identity 2010 as a underlying tool for SharePoint profiles synchronization.
This is a worst change in SharePoint from previous version. Now, It’s very slow, very hard-configurable and causes huge amount of problems during configure procedures and further work. To verify this you can read these guides: Technet: Configure profile synchronization, Technet: Maintain profile synchronization.
Quote: Incorrect permissions are the most common cause of errors in configuring profile synchronization
Quote: Farm Account must Be a member of the Administrators group on the synchronization server (see the Profile Synchronization Planning worksheet). You can remove this permission after you have configured the User Profile Synchronization service.
Wonderful, isn’t it?
And now We’ll talk about setting up an Active Directory connection with user profiles. You can configure such connection with administrative pages of User Profile Service Application, but these pages are very slow and buggy.
Note: For example, if Active Directory has some granular security settings you may get a timeout error as described here. You can configure timeouts with powershell (here, last paragraph), but that doesn’t help in this case.
We must choose another way: let’s automate the creation of connections with great help of PowerShell gods.
First of all, the core of our script is the UserProfileConfigManager class that contains the ConnectionManager property with several methods for adding new connections with user profiles.
We are interested in the AddActiveDirectoryConnection method, of course. This is a very “simple” method with 8 parameters (!) .
public DirectoryServiceConnection AddActiveDirectoryConnection(
ConnectionType type,
string displayName,
string server,
bool useSSL,
string accountDomain,
string accountUsername,
SecureString accountPassword,
List<DirectoryServiceNamingContext> namingContexts,
string spsClaimProviderTypeValue,
string spsClaimProviderIdValue
)
And here there is another type DirectoryServiceNamingContext which has importance for us.
Its constructors have large signatures too – 8 and 9 parameters:
public DirectoryServiceNamingContext(
string distinguishedName,
string domainName,
bool isDomain,
Guid objectId,
List<string> containersIncluded,
List<string> containersExcluded,
List<string> preferredDomainControllers,
bool useOnlyPreferredDomainControllers
)
Thanks God, we have intuitive parameter names and types. So, let’s code our script:
Add-SPActiveDirectoryConnection.ps1:
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
function Get-SPServiceContext(
[Microsoft.SharePoint.Administration.SPServiceApplication]$profileApp)
{
if($profileApp -eq $null)
{
# Get first User Profile Service Application
$profileApp = @(Get-SPServiceApplication |
? { $_.TypeName -eq "User Profile Service Application" })[0]
}
return [Microsoft.SharePoint.SPServiceContext]::GetContext(
$profileApp.ServiceApplicationProxyGroup,
[Microsoft.SharePoint.SPSiteSubscriptionIdentifier]::Default)
}
function Convert-ToList($inputObject, [System.String]$Type)
{
begin
{
if($type -eq $null -or $type -eq '')
{
$type = [string]
}
$list = New-Object System.Collections.Generic.List[$type]
}
process { $list.Add($_) }
end
{
return ,$list
}
}
function Get-DC($domainName)
{
return ("DC=" + $domainName.Replace(".", ",DC="))
}
# Types
$DirectoryServiceNamingContextType = [Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext, Microsoft.Office.Server.UserProfiles, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]
# Globals
$connectionName = "example.com"
$domainName = "example.com"
$accountName = "EXAMPLE\User"
$password = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$partitions = @{
"example.com" = @("DC=example,DC=com");
};
# Main()
# Prepare Parameters
$userDomain = $accountName.Substring(0, $accountName.IndexOf("\"))
$userName = $accountName.Substring($accountName.IndexOf("\") + 1)
$dnContexts = $partitions.GetEnumerator() |
% {
$domainName = $_.Key
$containers = $_.Value | Convert-ToList
$partition = [ADSI]("LDAP://" + (Get-DC $domainName))
$partitionId = New-Object Guid($partition.objectGUID)
New-Object $DirectoryServiceNamingContextType(
$partition.distinguishedName,
$domainName,
<# isDomain: #> $false,
<# objectId: #> $partitionId,
<# containersIncluded: #> $containers,
<# containersExcluded: #> $null,
<# preferredDomainControllers: #> $null,
<# useOnlyPreferredDomainControllers: #> $false)
} | Convert-ToList -Type $DirectoryServiceNamingContextType
$partition = [ADSI]("LDAP://CN=Configuration," + (Get-DC $domainName))
$partitionId = New-Object Guid($partition.objectGUID)
$containers = @($partition.distinguishedName) | Convert-ToList
$dnContext = New-Object $DirectoryServiceNamingContextType(
$partition.distinguishedName,
$domainName,
<# isDomain: #> $true,
<# objectId: #> $partitionId,
<# containersIncluded: #> $containers,
<# containersExcluded: #> $null,
<# preferredDomainControllers: #> $null,
<# useOnlyPreferredDomainControllers: #> $false)
$dnContexts.Add($dnContext)
# Create Active Directory Connection
$serviceContext = Get-SPServiceContext
$configManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($serviceContext)
if($configManager.ConnectionManager.Contains($connectionName) -eq $false)
{
$configManager.ConnectionManager.AddActiveDirectoryConnection(
[Microsoft.Office.Server.UserProfiles.ConnectionType]::ActiveDirectory,
$connectionName, $domainName, <# useSSL: #> $false,
$userDomain, $userName, $password,
<# namingContexts #> $dnContexts,
<# spsClaimProviderTypeValue: #> $null,
<# spsClaimProviderIdValue: #> $null)
}
else
{
Write-Host "Connection '$connectionName' already exist. Delete it before run this script."
}
That’s All.
And, of course, I hate it!