I’ve been operating under the Bitstream Foundry banner for a few months now, and I have to admit that it’s been absolutely wonderful to return to hands-on work with SharePoint. It really didn’t take long for me to realize just how much I missed solving problems and making SharePoint do what customers/clients wanted it to do. Some of you may think I’m crazy for saying this, but it’s good to be back in the trenches.
And the Cycle Begins Again
A little background for this post: in the early days of SharePoint 2010, it didn’t take much exposure for me to sour on the User Profile Service (UPS) and User Profile Service Application (UPA).
Brief note: I lump the UPS, UPA, and related pieces under the “UPS” acronym for this post. It’s not technically accurate, but it keeps things simpler for readability.
The UPS was buggy, problematic, and easily broke if you looked at it the wrong way. I know that the bugs were ultimately ironed-out and that the UPS was “fixed” to a large extent, but I stopped playing with it before it got to that point. I never really needed it for anything I wanted to do, and so I did my best ostrich impersonation anytime anything related to the User Profile Service and its underlying plumbing came up.
It’s somewhat ironic, then, that I was only back into hands-on work for about a month before I found myself thrown into the UPS with SharePoint 2013. A client wanted me to put together a script that they could use to perform the post-setup configuration for the UPS in a farm once the UPS instance had been created (through AutoSPInstaller – which also happens to be my “go to” tool for SharePoint setup). The client wanted the script to configure built-in profile properties, create custom properties, create user sub-types, and most importantly: configure synchronization connections. End-to-end, the script needed to do everything that an admin might normally have to do by hand in order to get the UPS ready for production usage.
The Twist
UPS configuration via PowerShell isn’t new territory. There’s a pretty substantial base of online material to draw from for guidance and inspiration (starting with TechNet), but my task was novel in one very important regard: the synchronization connections I was setting up were using SharePoint 2013’s new Active Directory Import mode.
If you’re not familiar with Active Directory Import mode (“AD Direct Mode”), pause your reading here and head over to Spence Harbar’s blog for a thorough and extremely well-written overview. It is a top-notch post on Active Directory Import Mode and provides a foundation for the rest of this post.
If you’ve scripted-out the creation of UPS synchronization connections through PowerShell under SharePoint 2010, then you’re undoubtedly familiar with the Add-SPProfileSyncConnection cmdlet and its syntax (taken straight from TechNet):
[sourcecode language=”powershell” toolbar=”false” wraplines=”true”]
Add-SPProfileSyncConnection [-ProfileServiceApplication] <SPServiceApplicationPipeBind> -ConnectionDomain <String> -ConnectionForestName <String> -ConnectionPassword <SecureString> -ConnectionSynchronizationOU <String> -ConnectionUserName <String> [-AssignmentCollection <SPAssignmentCollection>] [-Confirm [<SwitchParameter>]] [-ConnectionClaimIDMapAttribute <String>] [-ConnectionClaimProviderIdValue <String>] [-ConnectionClaimProviderTypeValue <String>] [-ConnectionNamingContext <String>] [-ConnectionPort <Int32>] [-ConnectionServerName <String>] [-ConnectionUseDisabledFilter <$true | $false>] [-ConnectionUseSSL <$true | $false>] [-WhatIf [<SwitchParameter>]]
[/sourcecode]
As I started putting together my script and looking for usage examples covering the Add-SPProfileSyncConnection cmdlet, I naturally came to another of Spence Harbar’s posts. Spence’s post was the first sign of trouble for me. Even though Spence’s post was written in the SharePoint 2010 time frame, he noted a number of limitations with the Add-SPProfileSyncConnection cmdlet versus creating sync connections within Central Administration. For example, there was no way to specify a DisplayName parameter with Add-SPProfileSyncConnection; you were stuck with a name that was automatically assigned based on the connection’s associated domain name.
The Wall
The DisplayName limitation was annoying, but it wasn’t a show-stopper for me. With a little more investigation, though, the real roadblocks quickly made themselves known.
If you were to compare the fields and options that are available when adding a sync connection through SharePoint Central Administration (shown on the right) with those that are available when adding a connection with the Add-SPProfileSyncConnection, you’d see some significant gaps. The ones that stood between me and a configuration script that did what I needed it to do were the following:
- The inability to use the “Filter out disabled users” switch
- The inability to specify an LDAP filter to restrict imported users
The ability to filter disabled users was really a convenience; I could just as easily extend my LDAP filter to include the following:
(!userAccountControl:1.2.840.113556.1.4.803:=2)
… if I wanted to filter disabled users without using the checkbox that was available in Central Admin. Without the ability to specify an LDAP filter in the Add-SPProfileSyncConnection call, though, I was stuck. I needed some way to specify an LDAP filter to restrict imported users for the connections I was going to be creating.
I spent a lot of time hunting through forums and looking for information online, but the hunt proved fruitless. No matter how I approached the problem and tinkered with PowerShell, I came up empty. Falling back to the SharePoint Server Object Model, the ConnectionManager class (in the Microsoft.Office.Server.UserProfiles namespace), and its related types got me no further, either. Absolutely nothing in the APIs seemed to expose a way to add Active Directory Import sync connections in a way that exposed the properties I needed to specify.
I had a hard time believing it, but it seemed that the only way to create an Active Directory Import-based sync connection that allowed me to specify a DisplayName and LDAP filter was to use SharePoint Central Administration.
Huh?
The Source of the Problem
Since Central Administration supplied the mechanism to create sync connections with the desired parameters, I knew that SharePoint had the plumbing to support what I wanted to do somewhere in its assemblies. I just needed to figure out where that functionality was. So, I did what I typically do in these sorts of situations: I pulled out Reflector and went diving into SharePoint’s internals.
I took a look at both the EditDSServer.aspx page in the _layouts/15 folder (which is the Central Admin page for creating sync connections) and the Add-SPProfileSyncConnection cmdlet, did some backtracking, and eventually found myself back at the ConnectionManager class – no big surprise there. The somewhat surprising part, though, was what I found (shown to the left).
It appeared that the ConnectionManager class sported several methods to support the addition of sync connections: AddActiveDirectoryConnection, AddActiveDirectoryImportConnection, AddBusinessDataCatalogConnection, and AddLdapConnection. The one that I was interested in was the second one – AddActiveDirectoryImportConnection:
[sourcecode language=”csharp” toolbar=”false” wraplines=”true”]
internal void AddActiveDirectoryImportConnection(ConnectionType type, Guid dcId, string displayName, string server, bool useSSL, bool useDisabledFilter, string ldapFilter, string accountDomain, string accountUsername, SecureString accountPassword, List<DirectoryServiceNamingContext> namingContexts, string spsClaimProviderTypeValue, string spsClaimProviderIdValue, string adClaimIDMapAttribute)
[/sourcecode]
The big problem was that method was marked internal – meaning it wasn’t publicly exposed to outside callers.
Tracing things through, I found that the AddActiveDirectoryImportConnection method was ultimately accessible through two different public entry points: the Central Admin EditDSServer.aspx page and the Add-SPProfileSyncConnection PowerShell cmdlet. The EditDSServer.aspx page call was pretty direct, but the PowerShell cmdlet call ended up channeling through the UpdateCreateSyncConnection method on the Microsoft.Office.Server.Administration.UserProfileApplication class before making its way to the AddActiveDirectoryImportConnection method. And the UpdateCreateSyncConnection method was the source of the problem.
UpdateCreateSyncConnection is a method that coordinates the creation and modification of different sync connection types – not just the Active Directory Import connection type I was focused on. Most of the parameters that are needed to create a sync connection are available on the method signature, but not all of them.
[sourcecode language=”csharp” toolbar=”false” wraplines=”true”]
internal bool UpdateCreateSyncConnection(string connectionName, string connectionForestName, string connectionDomain, string connectionUserName, SecureString connectionPassword, string connectionServerName, int connectionPort, bool connectionUseSSL, bool connectionUseDisabledFilter, string connectionNamingContext, string connectionSynchronizationOU, string connectionClaimProviderTypeValue, string connectionClaimProviderIdValue, string adClaimIDMapAttribute, bool connectionFilterOutUnlicensed)
[/sourcecode]
Notice the lack of any LDAP filter parameter.
Looking through the method to where it actually calls into the AddActiveDirectoryImportConnection internal method reveals how it handles passing-in an LDAP filter … or rather, how it doesn’t handle it:
[sourcecode language=”csharp” toolbar=”false” wraplines=”true”]
connectionManager.AddActiveDirectoryImportConnection(ConnectionType.ActiveDirectoryImport, Guid.NewGuid(), connectionName, connectionForestName, connectionUseSSL, connectionUseDisabledFilter, string.Empty, connectionDomain, connectionUserName, connectionPassword, namingContexts, filterOutUnlicensed, connectionClaimProviderTypeValue, connectionClaimProviderIdValue, adClaimIDMapAttribute);
[/sourcecode]
See the string.Empty parameter in the method call? It’s in the same position where the AddActiveDirectoryImportConnection method expects an LDAP filter. The UpdateCreateSyncConnection method basically “drops the ball” and does absolutely nothing with an LDAP filter. If code had attitude, I envisioned this as the UpdateCreateSyncConnection method throwing up its virtual arms and saying “whatever.”
That crashing noise? My efforts thus far slamming into a wall.
Working Around the Limitation
Since I’d officially proven (to myself, anyway) that the Add-SPProfileSyncConnection cmdlet wasn’t going to work for my purposes, and I wasn’t going to be able to script out the desired action through Central Administration, I was looking at going against the AddActiveDirectoryImportConnection method myself directly from within my PowerShell script.
Insert a giant “ewwwwwwwww” sound here. Why? Because the method was marked internal. Since the method wasn’t publicly accessible, I’d need to use Reflection to call it.
I’ve used Reflection when I’ve had to in the past, and it certainly works, but it’s always had something of a “dark art” feel to me. And the times when I have used Reflection heavily have been within managed C# code – not PowerShell. Reflection is very persnickety regarding object types and method signatures, and C# (with its strict typing) makes it relatively painless to manage these issues. PowerShell, on the other hand, is pretty loose with typing and coercion – not ideal for working with Reflection-based parameter specification and method invocation.
Dark arts or not, I was bound and determined to provision my sync connections through PowerShell. So onward I marched.
The Result
I’m happy to say that this story does have a happy ending. The script that I’m sharing (Add-SPProfileDirectSyncConnection) to provision Active Directory Import connections appears below. It’s not the prettiest thing around, but hey – it works.
A word of caution before you pluck this out and start using it: the script goes directly against an internal method on the ConnectionManager class instead of a publicly exposed method, so I want to stress that this is a “use at your own risk” script. Anytime I have the option of using publicly accessible APIs, I do … but I had no other choice in this case.
[sourcecode language=”powershell” toolbar=”true” wraplines=”true”]
<#
.SYNOPSIS
Add-SPProfileDirectSyncConnection.ps1
.DESCRIPTION
Establishes a new Active Directory Direct user profile synchronization connection for SharePoint 2013.
.NOTES
Author: Sean McDonough
Last Revision: 27-June-2013
#>
param (
[parameter(mandatory=$true,position=1)][object]$userProfileApp,
[parameter(mandatory=$true)][string]$displayName,
[parameter(mandatory=$true)][string]$forestName,
[parameter(mandatory=$true)][string]$syncOU,
[parameter(mandatory=$false)][switch]$useSsl = $false,
[parameter(mandatory=$false)][switch]$useDisabledFilter = $false,
[parameter(mandatory=$false)][string]$ldapFilter = "",
[parameter(mandatory=$true)][string]$domain,
[parameter(mandatory=$true)][string]$username,
[parameter(mandatory=$true)][string]$password,
[parameter(mandatory=$false)][string]$claimProviderType = "Windows",
[parameter(mandatory=$false)][string]$claimProviderId = "Windows",
[parameter(mandatory=$false)][string]$claimIdMapAttribute = "samAccountName"
)
# Pseudo-constants
$DEFAULT_SITE_SUBSCRIPTION_ID = [Guid]::Empty
$INVOKE_ATTRIBUTES_NON_PUBLIC_MEMBERS = ([System.Reflection.BindingFlags]::NonPublic -bOr [System.Reflection.BindingFlags]::Instance)
# Handles all activities associated with direct-mode UPA sync connection provisioning
function ProvisionDirectSyncConnection($userProfileApp, $displayName, $forestName, $syncOU, $useSsl, $useDisabledFilter, $ldapFilter, $domain, $username, $password, $claimProviderType, $claimProviderId, $claimIdMapAttribute)
{
try
{
Write-Host "Preparing to create new synchronization connection: " -NoNewline
Write-Host $displayName -ForegroundColor blue
$serviceContext = [Microsoft.SharePoint.SPServiceContext]::GetContext($userProfileApp.ServiceApplicationProxyGroup, $DEFAULT_SITE_SUBSCRIPTION_ID)
$upConfigMgr = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($serviceContext)
if ($upConfigMgr.ConnectionManager.Contains($displayName)) {
Write-Host (" – Sync connection already exists. No action taken.") -ForegroundColor yellow
} else {
# Some additional parameter setup and cleanup
$connType = [Microsoft.Office.Server.UserProfiles.ConnectionType]::ActiveDirectoryImport
$dcId = [Guid]::NewGuid()
$securePassword = [System.Security.SecureString](ConvertTo-SecureString $password -AsPlainText -Force)
if ($useSsl) {
$isSslUsed = $true
} else {
$isSslUsed = $false
}
if ($useDisabledFilter) {
$isDisabledFilterUsed = $true
} else {
$isDisabledFilterUsed = $false
}
# Parameters needed for naming context creation
$isDomain = $true
$excludedOU = New-Object System.Collections.Generic.List[[System.String]]
$includedOU = New-Object System.Collections.Generic.List[[System.String]]
$includedOU.Add($syncOU)
$filterOutUnlicensed = New-Object System.Collections.Generic.List[[System.Boolean]]
$filterOutUnlicensed.Add($false)
$preferredDCs = New-Object System.Collections.Generic.List[[System.String]]
$useOnlyPreferredDCs = $false
# Perform an LDAP lookup to get the object ID for the target domain.
$ldapLookupContext = "LDAP://" + $forestName
$ldapUsername = $domain + "\" + $username
$objDomain = New-Object System.DirectoryServices.DirectoryEntry($ldapLookupContext, $ldapUsername, $password)
if ($useSsl) {
$objDomain.AuthenticationType = [System.DirectoryServices.AuthenticationTypes]::SecureSocketsLayer
}
$ldapDomainDn = $objDomain.distinguishedName
$ldapDomainGuid = New-Object Guid($objDomain.objectGUID)
# Creation of the objects needed to properly specify the OU for the sync connection
$dnCtx = New-Object Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext(
$ldapDomainDn, $forestName, $isDomain, $ldapDomainGuid, $includedOU, $excludedOU, $preferredDCs, $useOnlyPreferredDCs)
$namingContext = New-Object System.Collections.Generic.List[[Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext]]
$namingContext.Add($dnCtx)
$ncParam = [System.Collections.Generic.List[Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext]]$namingContext
# Since the method we’re about to invoke is internal, some hoops have to be jumped through
# to call it via PowerShell and Reflection
$paramTypes = @([Microsoft.Office.Server.UserProfiles.ConnectionType], [System.Guid], `
[System.String], [System.String], [System.Boolean], [System.Boolean], [System.String], `
[System.String], [System.String], [System.Security.SecureString], `
[System.Collections.Generic.List[Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext]], `
[System.String], [System.String], [System.String])
$addConnMethodInfo = [Microsoft.Office.Server.UserProfiles.ConnectionManager].GetMethod("AddActiveDirectoryImportConnection", `
$INVOKE_ATTRIBUTES_NON_PUBLIC_MEMBERS, $null, $paramTypes, $null)
$methodParams = @($connType, $dcId, $displayName, $forestName, $isSslUsed, $isDisabledFilterUsed, $ldapFilter, `
$domain, $username, $securePassword, $ncParam, $claimProviderType, $claimProviderId, $claimIdMapAttribute)
$addConnMethodInfo.Invoke($upConfigMgr.ConnectionManager, $methodParams)
#Still here? Looks like everything worked.
Write-Host (" – Sync connection successfully provisioned.") -ForegroundColor green
}
}
catch [Exception] {
Write-Error $Error[0]
$err = $_.Exception
while ( $err.InnerException ) {
$err = $err.InnerException
Write-Host $err.Message
}
}
}
# Ensure SP PS snap-in is loaded and launch provisioning method
$spCmdlets = Get-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction silentlycontinue
if ($spCmdlets -eq $null) {
Add-PSSnapin Microsoft.SharePoint.PowerShell
}
ProvisionDirectSyncConnection $userProfileApp $displayName $forestName $syncOU $useSsl $useDisabledFilter $ldapFilter $domain $username $password $claimProviderType $claimProviderId $claimIdMapAttribute
[/sourcecode]
The script accepts a variety of parameters and has a couple of switches:
- -userProfileApp. A reference to the User Profile Service Application instance for which the sync connection will be provisioned. This parameter is required.
- -displayName. A string containing the “friendly name” that will appear for the sync connection within Central Administration. This parameter is required.
- -forestName. A string that contains the fully qualified domain name (FQDN) for the domain that hosts the user profiles that will be imported by the sync connection. This parameter is required.
- -syncOU. A string that contains the full distinguished specification of the Active Directory organizational unit (OU) where target user accounts (for import) reside. This parameter is required.
- -useSsl. A switch that can be used to direct the script to use Secure Sockets Layer (SSL) transport encryption when connecting to Active Directory for synchronization operations and LDAP lookups. If this switch is not specified, connections are not made using SSL.
- -useDisabledFilter. If this switch is included, the user profile selection process that takes place for the sync connection being established will filter out user accounts that are disabled. If this switch is not specified, both active and inactive user accounts will be imported during synchronization.
- -ldapFilter. A string that contains an LDAP (Lightweight Directory Access Protocol) filter that will be applied to restrict the accounts which will be imported during synchronization. Creating this type of filter is made easier with the use of a tool like ADSI Edit. This parameter is optional.
- -domain. A string containing the NetBIOS name of the domain where the profile synchronization account resides. This string is combined with the –username parameter to form a domain + username account specification. This parameter is required.
- -username. A string containing the username of the profile synchronization account. This parameter should include the username only without a domain name qualifier. A domain name is specified separately through the –domain parameter. This parameter is required.
- -password. A string containing the plaintext password of the profile synchronization account. This parameter is required.
- -claimProviderType. A string identifying the type of authentication provider that is used to encode user profile account names. Common values for this parameter are Windows and Trusted. This parameter is optional; if unspecified, a value of Windows is used.
- -claimProviderId. A string specifying the name or ID of the claims provider in-use. When a –claimProviderType of Windows is used, this parameter value is also typically Windows. If a –claimProviderType of Trusted is used, then this parameter value is used in conjunction with that value to identify the specific auth provider instance used. This parameter is optional and will default to Windows if unspecified.
- -claimIdMapAttribute. A string that specifies the claims ID or field used by the claim provider. This parameter is specific to the claim provider, and in Windows authentication scenarios will usually be samAccountName. This parameter is optional.
The following is an example of how you might use the Add-SPProfileDirectSyncConnection script in a larger script that provisions user profile Active Directory Direct sync connections.
[sourcecode language=”powershell” toolbar=”true” wraplines=”true”]
$spCmdlets = Get-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction silentlycontinue
if ($spCmdlets -eq $Null) { Add-PSSnapin Microsoft.SharePoint.PowerShell }
$upa = Get-SPServiceApplication | where {$_.TypeName -eq "User Profile Service Application"}
Add-SPProfileDirectSyncConnection.ps1 -userProfileApp $upa `
-displayName "Domain Users" `
-forestName "testdomain.com" `
-syncOU "OU=Test Domain Users,DC=testdomain,DC=com" `
-useDisabledFilter `
-ldapFilter "(&(objectCategory=person)(objectClass=user)(mail=*)(!userAccountControl:1.2.840.113556.1.4.803:=65536))" `
-domain "TESTDOMAIN" `
-username "spProfileSync" `
-password "Pa$$word1"
[/sourcecode]
Some tips and information regarding the script and its usage:
Before executing this script, ensure that the account under which the script is being run has Full Control connection permission to the User Profile Service Application. This is set by highlighting the UPA instance in the “Manage Service Applications” page (ServiceApplications.aspx) in Central Admin, clicking the Permissions button on the toolbar, and granting Full Control permissions to the desired account as shown on the right. If this step is not performed, the script will error-out.
- If a sync connection with the supplied –displayName already exists, the script will exit. It will not attempt to create another connection with the same name.
- Ensure that the user profile synchronization account specified has the necessary replication permissions with the target user profile store. If the account lacks these permissions, the sync connection will be properly established but profiles will not be imported. Both TechNet and Spence Harbar covers this permission requirement in their various articles/posts, and it’s the same one that was needed in SharePoint 2010.
Final Word
My goal with this post was to (hopefully) save some of you the hassle I encountered while trying to automate a task that I thought would have been trivial … but wasn’t. Please let me know if this ends up helping you!
And although this script has been tested, I don’t have the facilities to test every combination of parameter and environment. If you find something that’s wrong or should be refactored, please let me know and I’ll perform an update.
Have fun!
References and Resources
- Company: Bitstream Foundry
- TechNet: Overview of the User Profile service application in SharePoint Server 2013
- CodePlex: AutoSPInstaller
- TechNet: Use Windows PowerShell cmdlets to configure the User Profile service in SharePoint Server 2013
- Harbar.net: First Look: SharePoint Server 2013 Active Directory Import
- TechNet: Add-SPProfileSyncConnection
- Harbar.net: Managing Sync Connections with *-SPProfileSyncConnection cmdlets in Service Pack 1
- TechNet Windows Server Forums: LDAP Query for all active users
- MSDN: ConnectionManager class
- Red Gate Software: .NET Reflector 8
- MSDN: Reflection in the .NET Framework
- TechNet: ADSI Edit (adsiedit.msc)
Yes there are some issues with that commandlet. If you want to dive into some other fun headaches try out the command that removes the sync connections… Great post though.
Ha! I can only climb one hill at a time, Brian, and I’m still tired after the last one :-) Thanks for the feedback; always great to hear from you!
Hey Sean,
Nice Article. I tried this script but I am getting a error. This is the stack trace I was able to retrieve.
at Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportMappingCollection.GetProtectedKey(UserProfileApplication upa)
at Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportMappingCollection.Add(Guid dcId, String connectionName, String dcName, String rootDn, String filter, Boolean useSSL, Boolean useDisabledFilter, String domainName, String userName, SecureString password, Byte[] syncCookie)
at Microsoft.Office.Server.UserProfiles.ActiveDirectoryImportConnection.UpdateInternal()
at Microsoft.Office.Server.UserProfiles.ConnectionManager.AddActiveDirectoryImportConnection(ConnectionType type, Guid dcId, String displayName, String server, Boolean useSSL, Boolean useDisabledFilter, String ldapFilter, String accountDomain, String accountUsername, SecureString accountPassword, List`1 namingContexts, List`1 filterOutUnlicensed, String spsClaimProviderTypeValue, String spsClaimProviderIdValue, String adClaimIDMapAttribute)
at Microsoft.Office.Server.UserProfiles.ConnectionManager.AddActiveDirectoryImportConnection(ConnectionType type, Guid dcId, String displayName, String server, Boolean useSSL, Boolean useDisabledFilter, String ldapFilter, String accountDomain, String accountUsername, SecureString accountPassword, List`1 namingContexts, String spsClaimProviderTypeValue, String spsClaimProviderIdValue, String adClaimIDMapAttribute)
Used the exact script you have in this blog. This is the inpiut
.\Add-ADSyncConnectionOrg.ps1 -userProfileApp $upa `
-displayName “Domain Users” `
-forestName “abc.xyz.com” `
-syncOU “OU=Admins,DC=domain1,DC=domain2,DC=com” `
-useDisabledFilter `
-ldapFilter “(&(objectCategory=person)(objectClass=user))” `
-domain “domain” `
-username “account” `
-password “something”
Verified the $upa that being passed is not null.
Can you let me know what I am doing wrong? Or any place I should be looking to possibly resolve this issue? Thanks!
Hi Senthil,
Have you verified that your account has Full Control connection permission to UPA instance as described in the article? Even if you’re a farm administrator and/or management shell admin, you won’t have this permission until you explicitly grant it to your account. Without that permission, the script will throw an exception at the point at which it attempts to create a new object of type Microsoft.Office.Server.UserProfiles.UserProfileConfigManager.
If you’re getting past that point, verify that you don’t already have an established connection to the domain or that is called “Domain Users” (to use your example). AD Import only supports one connection per domain, I believe, so you may be getting a collision if it already sees a connection there. The script doesn’t include detection logic to avoid the collision, but it wouldn’t be too hard to build that in if you chose to do so.
I hope that helps!
– Sean
Hello,
Thanks for sharing us this useful article and pretty well explained :D.
First I check that the account which launch the script have full permissions on the User profile.
I have such an issue when trying to use it.
When I tried to use it I got this error :
ProvisionDirectSyncConnection : Exception calling Invoke with 2 argument (s): Object reference is not set to an instance of an object.
At C:\CreateADConnection\Add-SPProfileDirectSyncConnection.ps1:119 : 1
+ ProvisionDirectSyncConnection $userProfileApp $displayName $forestName $syncOU $ …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,ProvisionDirectSyncConnection
I managed to get the full StackTrace :
$ex.Exception.InnerException.StackTrace
à Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportMappingCollection.GetProtectedKey(UserProfileApplication upa)
à Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportMappingCollection.Add(Guid dcId, String connectionName, String dcName, String rootDn, St
ring filter, Boolean useSSL, Boolean useDisabledFilter, String domainName, String userName, SecureString password, Byte[] syncCookie)
à Microsoft.Office.Server.UserProfiles.ActiveDirectoryImportConnection.UpdateInternal()
à Microsoft.Office.Server.UserProfiles.ConnectionManager.AddActiveDirectoryImportConnection(ConnectionType type, Guid dcId, String displayName, String serv
er, Boolean useSSL, Boolean useDisabledFilter, String ldapFilter, String accountDomain, String accountUsername, SecureString accountPassword, List`1 namingCon
texts, List`1 filterOutUnlicensed, String spsClaimProviderTypeValue, String spsClaimProviderIdValue, String adClaimIDMapAttribute)
à Microsoft.Office.Server.UserProfiles.ConnectionManager.AddActiveDirectoryImportConnection(ConnectionType type, Guid dcId, String displayName, String serv
er, Boolean useSSL, Boolean useDisabledFilter, String ldapFilter, String accountDomain, String accountUsername, SecureString accountPassword, List`1 namingCon
texts, String spsClaimProviderTypeValue, String spsClaimProviderIdValue, String adClaimIDMapAttribute)
Do you have an idea to solve that issue ?
Thanks a lot :)
That is the same error I get. Script looks great, but there is a small issue somewhere in what was posted.
This is the same error I am getting. Sean, the script looks great, but there appears to be a small issue somewhere. Can you verify the code post is the latest tested and confirmed to work?
Same for me, but I am running the script in SP2010/Powershell 2 environment. I amended the script to make it working with SP2010 (the signature of AddActiveDirectoryImportConnection is different) but then I can’t go past this error. Any advice?
Andy: this is the latest version of the script; I haven’t touched it since it was released a year and a half ago. If there’s a bug in it, I haven’t seen it surface in my usage. I’m sorry.
Micky: If you’re attempting to use this script in SharePoint 2010, then I can pretty much guarantee that you’re going to have more than a few issues. Active Directory Import mode doesn’t exist in SharePoint 2010; you’re *required* to provision synchronization connections through FIM, so this script isn’t going to be of much use to you. Sorry.
Sean, thanks for your reply. Actually I am using FIM in the background, but the Add-SPProfileSyncConnection cmdlet exists in SP2010, and it uses the same methods. It doesn’t have the LDAP filter and the disabled filter but it should be able to configure a connection anyway which is my goal. I would like to be able to replicate my profile sync connection in all the environments I have, without using that terrible UI
I follow what you’re saying, Micky. Although there’s a single cmdlet to provision the sync connection, the underlying implementation is very different for FIM vs. Active Directory Import mode; they’re very different code paths if you use something like Reflector to look under-the-hood. I suspect you’ll need to do some deeper diving to see what needs to change from a method calling and supplied parameters perspective. I’m sorry that I don’t have any guidance to provide in this regard, but I wish you the best of luck!
Hi Sean, many thanks for this great post. Am I right in thinking that the *-SPProfileSyncConnection cmdlets are still officially unsupported, per http://www.harbar.net/archive/2011/07/12/332.aspx ? I know that post is a little out of date so wasn’t sure if things have changed.
Hey Ben! As best as I’ve been able to tell, the “unsupported” qualifier doesn’t exist for the *-SPProfileSyncConnection cmdlets any longer. If you check TechNet (http://technet.microsoft.com/en-us/library/jj219677.aspx), for example, you won’t see any warning called out. While that’s not the same as coming out and saying “fully supported,” I take the lack of a warning to mean that they’re probably okay to use.
Now, whether or not you’d want to use the cmdlets is another case entirely. Given some of the limitations and internal hacks I’ve seen and described (based on reflecting into the cmdlets and their supporting methods), it’s best to understand what you’re getting into and assuming before use – especially with the Add-SPProfileSyncConnection. In the scenario I described in this post, there were some clear gaps between what I needed to do and how the cmdlet operated. If those gaps didn’t exist, would I have chosen to use the cmdlets? Probably. I’m not a fan of re-inventing the wheel, so I like to use out-of-the-box (OOTB) where possible. When OOTB doesn’t cut it, though, I end up working around the limitations – and writing blog posts like this one, of course :-)
Hi,
The connection type is for ActiveDirectoryImport.
Can we set up filters when the connection type is ActiveDirectory.
I have the farm configured to Full sharepoint config connections.
Thanks,
Ankit
In all honesty, Ankit, I don’t know. I get the sense that some folks have read this post and are trying to use this script to either (a) provision a connection on SharePoint 2010, or (b) provision an Active Directory (not an Active Directory Import, or “AD Direct”) connection that goes through FIM. That’s not what this script was built to do, and I can all but guarantee that there will be issues with the script as-is.
I noticed that your last comment is a couple years ago. Seems like this is the only relevant infomation I can find though. https://technet.microsoft.com/en-us/library/jj219677.aspx is now a blank page with the words “This Topic Is No Longer Available”. I’m looking for something on SharePoint 2016 on-premises. Do you know of anything like it?
Ugh – I hate when that happens. I’m quickly finding that in addition to a link, I need to post a screenshot *just in case* the material disappears.
Yeah, it was quite some time ago when I wrangled with this. Since that time, I’ve done nearly nothing new with the UPS. It’s just not an area I spend any time in. I’m sorry :-(