October 12, 2010

HOWTO: Create BSC connection with User Profiles via PowerShell

As I described earlier, you can create synchronization connection with User Profiles for Active Directory via PowerShell. And what about the Business Connectivity Services? It’s also possible, of course!

As well as in the previous post, the core of our script is the UserProfileConfigManager class that contains the ConnectionManager property with several methods for adding new connections with user profiles.

Screenshot: UserProfileConfigManager class

The main difference that this time we'll use the AddBusinessCatalogConnection method which has eight parameters (it’s much more than almost any method in the .Net BCL).

public BusinessDataCatalogConnection AddBusinessDataCatalogConnection(
string
displayName,
string
systemName,
string
entityName,
string
entityNamespace,
string
profilePropertyName,
string
filterName,
string
mappedPropertyAttributeName, List<DSMLAttribute> attributeList )

There are simple parameters, except the last: the list of instances of DSMLAttribute (non-documented) class. This class is a simple set of properties, as you can see in the picture below.

Screenshot: DSMLAttribute class

Note: DSML – an acronym that means the Directory Services Markup Language. You can find more information about that on Wikipedia.

Before we begin, I assume that we already have an entity in the BCS model that has at least one method “Read Item” (Specific Finder). Identifier of the entity is a field containing a user account name (it will be mapped to the profile property “Account Name”).

Now, we can code our script. Listing below:

Add-BCSConnection.ps1

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

function Get-First { return @($input)[ 0] }
function Get-Last  { return @($input)[-1] }

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-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" } |
 
           
Get-First
    }
   
   
return [Microsoft.SharePoint.SPServiceContext]::
GetContext(
       
$profileApp.ServiceApplicationProxyGroup,
 
       
[Microsoft.SharePoint.SPSiteSubscriptionIdentifier]::
Default)
}


function Convert-ToDSMLSyntax([System.String]$typeName
) 
{
   
$DSMLSyntaxType = [Microsoft.Office.Server.UserProfiles.Synchronization.DSMLSyntax, Microsoft.Office.Server.UserProfiles.Synchronization, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]
   
   
if($typeName -match "System.DateTime"
)
    {
       
return $DSMLSyntaxType::
Date
    }
       
   
if($typeName -match "System.Int32"
)
    {
       
return $DSMLSyntaxType::
Integer
    }
   
   
if($typeName -match "System.Byte"
)
    {
       
return $DSMLSyntaxType::
Binary
    }
   
   
if($typeName -match "System.Boolean"
)
    {
       
return $DSMLSyntaxType::
Boolean
    }
       
   
return $DSMLSyntaxType::
String
}


# Global

$DSMLSyntaxType = [Microsoft.Office.Server.UserProfiles.Synchronization.DSMLSyntax, Microsoft.Office.Server.UserProfiles.Synchronization, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]
$DSMLAttributeType = [Microsoft.Office.Server.UserProfiles.Synchronization.DSMLAttribute, Microsoft.Office.Server.UserProfiles.Synchronization, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]

$connectionName = "BCS Connection"
$entityName = "ExternalUserInfo"
$entityNamespace = "ExternalUserInfoDatabase"
$profilePropertyName = "AccountName"

# Main()

$serviceContext = Get-SPServiceContext

# Prepare parameters

$entity = Get-SPBusinessDataCatalogMetadataObject -BdcObjectType Entity
 `
                                                 
-ServiceContext $serviceContext
 `
                                                 
-Name $entityName
 `
                                                 
-Namespace $entityNamespace
   
$entityIdentifier = $entity.Identifiers | Get-First

$specificFinder = $entity.Methods |
 
   
% { $_.MethodInstances } |
    ? { $_.MethodInstanceType -eq "SpecificFinder" } |
 
   
Get-First

$attrList = $specificFinder.ReturnTypeDescriptor.ChildTypeDescriptors |
 
   
%
 {
       
$attribute = New-Object $DSMLAttributeType
       
       
$attribute.ID = $_.
Name
       
$attribute.Name = $_.
Name
       
$attribute.Indexible = $false
        $attribute.SingleValued = $true
        $attribute.Syntax = Convert-ToDSMLSyntax($_.
TypeName)
       
       
$attribute
    } | Convert-ToList -Type $DSMLAttributeType

# Important: Special property for BCS connection!

$attribute = New-Object $DSMLAttributeType
   
$attribute.ID = "SPS-DistinguishedName"
$attribute.Name = "SPS-DistinguishedName"
$attribute.Indexible = 
$true
$attribute
.SingleValued = 
$true
$attribute
.Syntax = Convert-ToDSMLSyntax("System.String")

$attrList.Add($attribute);

# Create BCS Connection

$configMgr = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($serviceContext)
$connectionMgr = $configMgr.ConnectionManager

if(!$connectionMgr.Contains($connectionName
))
{
   
$connectionMgr.
AddBusinessDataCatalogConnection(
       
<# displayName: #> $connectionName,
 
       
<# systemName: #> $entity.LobSystem.Name,
 
       
<# entityName: #> $entity.Name,
 
       
<# entityNamespace: #> $entity.Namespace,
 
       
<# profilePropertyName: #> $profilePropertyName,
 
       
<# filterName: #> $null,
 
       
<# mappedPropertyAttributeName: #> $entityIdentifier.Name,
 
       
<# attributeList: #> $attrList)
}

Note: If you try to execute code and get error:

Exception calling "AddBusinessDataCatalogConnection" with "8" argument(s): "Could not connect to http://[server-name]:[random-port]/ResourceManagementService/MEX. TCP error co
de 10061: No connection could be made because the target machine actively refused it [ip-address]:[random-port]. "
At C:\[some-path]\Add-BCSConnection.ps1:+     $connectionMgr.AddBusinessDataCatalogConnection <<<< (
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

You must start Forefront Identity Manager Service (via services.msc console) and try again.

That’s all.

And, of course, I hate it!

2 comments:

  1. Hi andrew,
    So i have not been able to populate the container and i've followed the directions for permission correctly the message i'm getting when i run your script is
    Exception calling "AddActiveDirectoryConnection" with "10" argument(s): "Unable
    to process Create message"
    At C:\scripts\Add-SPActiveDirectoryConnection.ps1:111 char:66
    + $configManager.ConnectionManager.AddActiveDirectoryConnection <<<< (
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    ReplyDelete
  2. Hi Andrew,

    Thanks for you post, works great for me. I have one question though, You script creates a 1:1 mapping. Can you suggest what parameter I should change to create 1:many mapping ?

    ReplyDelete