harbar.net component based software & platform hygiene

Enabling multiple OUs and avoiding credential touch up with the MIMSync “toolset” for SharePoint Server 2016

Print | posted on Thursday, August 25, 2016 7:56 AM

As many of you are aware there is a “toolset” published on GitHub which provides one way to get up and running using Microsoft Identity Manager 2016 (MIM) for profile synchronization with Active Directory. This Windows PowerShell Module and exported MA configurations basically provisions a base capability more or less akin to what shipped with SharePoint 2013’s User Profile Synchronization capability.

I’m not much of a fan of this Module or it’s approach. Seriously, if a customer is going down the road of implementing MIM they better be sure they have the right skills in place – and right skills won’t be using this toolkit. Furthermore, the default property mappings etc are well, defaults. The less said the better frankly.

But of course there is the ye olde upgrade consideration. Customers who were using UPS need something to make it easier to move to MIM and of course there are also many without MIM experience (perhaps this year’s greatest understatement so far!). So there is a need for the Module despite it’s faults conceptually and in terms of the implementation (which are not the fault of the coder but consequences of bugs in the products).

Unfortunately due to the nature of the SharePoint Connector and bugs with MIM PowerShell cmdlets, the current version only supports a single container selection and also requires that the MA exports are “fixed” – i.e. with the details of the customer domain.

With the MIM hotfix rollup 4.3.2195.0 and changes to the module both of these issues can be avoided. It’s not a full “fix” and it’s not without a downside. However it is a much more practical “quick start”. Especially for customers with privileged access management policies enforced for administrators. The primary benefit thou is multiple container selection – the most common complaint I’ve received.

Once the hotfix update is installed on the MIM Sync box, the Install-SharePointSyncConfiguration function of the SharePointSync.psm1 module needs altered.

Basically, I’ve changed a a parameter name, removed the credential touch up work, and added a call to the MIM Set-MIISADMAConfiguration cmdlet after preparing the –Partitions parameter value. I’ve also updated the version check.

function Install-SharePointSyncConfiguration
{
<#
.Synopsis
   Configures the Synchronization Service for SharePoint User Profile Synchronization
.DESCRIPTION
   Long description
.EXAMPLE
   Install-SharePointSyncConfiguration -Path C:\SharePointSync -ForestDnsName litware.ca -ForestCredental (Get-Credential LITWARE\Administrator) –OrganizationalUnits 'ou=Litwarians,dc=Litware,dc=ca' -SharePointUrl http://SharePointServer:5555 -SharePointCredential (Get-Credential LITWARE\Administrator)
.EXAMPLE
    $spProps = @{
        Path                 = 'C:\Temp\SharePointSync'
        ForestDnsName        = 'litware.ca'
        ForestCredential     = New-Object PSCredential ("LITWARE\administrator", (ConvertTo-SecureString 'J$p1ter' -AsPlainText -Force))
        OrganizationalUnits   = 'ou=Legal,dc=Litware,dc=ca;ou=Litwarians,dc=Litware,dc=ca'
        SharePointUrl        = 'http://cmvm38386:9140'
        SharePointCredential = New-Object PSCredential ("LITWARE\administrator", (ConvertTo-SecureString 'J$p1ter' -AsPlainText -Force))
    }
    Install-SharePointSyncConfiguration @spProps -Verbose

#>
    [CmdletBinding()]
    [OutputType([int])]
    Param
    (
        # Path to the configuration XML files
        [Parameter(Mandatory=$true, Position=0)]
        $Path,

        # DNS name of the Active Directory forest to synchronize (ie - litware.ca)
        [Parameter(Mandatory=$true, Position=1)]
        $ForestDnsName,

        # Credential for connecting to Active Directory
        [Parameter(Mandatory=$true, Position=2)]
        [PSCredential]
        $ForestCredential,

        # OU(s) to synchronize to SharePoint (semi-colon delimited)
        [Parameter(Mandatory=$true, Position=3)]
        $OrganizationalUnits,

        # URL for SharePoint
        [Parameter(Mandatory=$true, Position=4)]
        [Uri]    
        $SharePointUrl,

        # Credential for connecting to SharePoint
        [Parameter(Mandatory=$true, Position=5)]
        [PSCredential]
        $SharePointCredential,

        # Flow Direction for Profile Pictures
        [Parameter(Mandatory=$false, Position=6)]
        [ValidateSet('Export only (NEVER from SharePoint)', 'Import only (ALWAYS from SharePoint)')]
        [String]
        $PictureFlowDirection = 'Export only (NEVER from SharePoint)'
    )

    #region Pre-requisites
    if (-not (Get-SynchronizationServiceRegistryKey))
    {
        throw "The Synchronization Service is not installed on this computer.  Please install the MIM Synchronization Service on this computer, or run this script on a computer where the MIM Synchronization Service is installed." 
    }

    if (-not (Get-Service -Name FimSynchronizationService))
    {
        throw "The Synchronization Service is installed but not running.  Please start the MIM Synchronization Service before running this script (Start-Service -Name FimSynchronizationService).  If the service fails to start please see the event log for details." 
    }

    if ((Test-SynchronizationServicePermission) -eq $false)
    {
        throw "The current user must be a member of the Synchronization Service Admins group before this command can be run.  You may need to logoff/logon before the group membership takes effect."
    }

    $MimPowerShellModuleAssembly = Get-Item -Path (Join-Path (Get-SynchronizationServicePath) UIShell\Microsoft.DirectoryServices.MetadirectoryServices.Config.dll)
    if ($MimPowerShellModuleAssembly.VersionInfo.ProductMajorPart -eq 4 -and
        $MimPowerShellModuleAssembly.VersionInfo.ProductMinorPart -eq 3 -and 
        $MimPowerShellModuleAssembly.VersionInfo.ProductBuildPart -ge 2195)
    {
        Write-Verbose "Sufficient MIM PowerShell version detected (>= 4.3.2195): $($MimPowerShellModuleAssembly.VersionInfo.ProductVersion)"
    }
    else
    {
        throw "SharePoint Sync requires MIM PowerShell version 4.3.2064 or greater (this version is currently installed: $($MimPowerShellModuleAssembly.VersionInfo.ProductVersion). Please install the latest MIM hotfix."
    }
    #endregion

    ### Load the Synchronization PowerShell snap-in
    Import-Module -Name (Join-Path (Get-SynchronizationServicePath) UIShell\Microsoft.DirectoryServices.MetadirectoryServices.Config.dll) 

    Write-Verbose "Contacting AD to get the partition details"
    $RootDSE                = [ADSI]"LDAP://$ForestDnsName/RootDSE"
    $DefaultNamingContext   = [ADSI]"LDAP://$($RootDSE.defaultNamingContext)"
    $ConfigurationPartition = [ADSI]"LDAP://$($RootDSE.configurationNamingContext)"

    Write-Verbose "Configuring the Active Directory Connector"
    Write-Verbose "  AD Forest:               $ForestDnsName"
    Write-Verbose "  AD OU:                   $OrganizationalUnit"
    Write-Verbose "  AD Credential:           $($ForestCredential.UserName)" 
    Write-Verbose "  AD Naming Partition:     $($RootDSE.defaultNamingContext)"
    Write-Verbose "  AD Config Partition:     $($RootDSE.configurationNamingContext)"

   
    $admaXmlFilePath = Join-Path $Path MA-ADMA.XML
    [xml]$admaXml = Get-Content -Path $admaXmlFilePath
    $admaXml.Save("$admaXmlFilePath.bak")

    ### Fix up the Domain partition
    $domainPartition = Select-Xml -Xml $admaXml -XPath "//ma-partition-data/partition[name='DC=Litware,DC=com']"
    $domainPartition.Node.name = $DefaultNamingContext.distinguishedName.ToString()
    $domainPartition.Node.'custom-data'.'adma-partition-data'.dn = $DefaultNamingContext.distinguishedName.ToString()
    $domainPartition.Node.'custom-data'.'adma-partition-data'.name = $ForestDnsName
    $domainPartition.Node.'custom-data'.'adma-partition-data'.guid = (New-Object guid $DefaultNamingContext.objectGUID).ToString('B').ToUpper() 
    $domainPartition.Node.filter.containers.inclusions.inclusion = $DefaultNamingContext.distinguishedName.ToString()
    $domainPartition.Node.filter.containers.exclusions.exclusion = $ConfigurationPartition.distinguishedName.ToString()

    ### Fix up the Configuration partition
    $configPartition = Select-Xml -Xml $admaXml -XPath "//ma-partition-data/partition[name='CN=Configuration,DC=Litware,DC=com']"
    $configPartition.Node.name = $ConfigurationPartition.distinguishedName.ToString()
    $configPartition.Node.'custom-data'.'adma-partition-data'.dn = $ConfigurationPartition.distinguishedName.ToString()
    $configPartition.Node.'custom-data'.'adma-partition-data'.name = $ForestDnsName
    $configPartition.Node.'custom-data'.'adma-partition-data'.guid = (New-Object guid $ConfigurationPartition.objectGUID).ToString('B').ToUpper() 
    $configPartition.Node.filter.containers.inclusions.inclusion = "CN=Partitions," + $ConfigurationPartition.distinguishedName.ToString()
   
   
    
    $admaXml.Save($admaXmlFilePath)
    
    Write-Verbose "Importing the Synchronization Service configuration"
    Write-Verbose "  Path: $Path"
    Import-MIISServerConfig -Path $Path -Verbose    

    # requires 3092179
    $Partitions = "$($RootDSE.defaultNamingContext);$($RootDSE.configurationNamingContext)"
    Set-MIISADMAConfiguration -MAName ADMA -Credentials $ForestCredential -Forest $ForestDnsName -Partitions $Partitions -Container $OrganizationalUnits -Verbose

    
    Write-Verbose "Configuring the SharePoint Connector"
    Write-Verbose "  SharePoint URL:          $SharePointUrl"
    Write-Verbose "  SharePoint Host:         $($SharePointUrl.Host)"
    Write-Verbose "  SharePoint Port:         $($SharePointUrl.Port)"
    Write-Verbose "  SharePoint Picture Flow: $PictureFlowDirection"
    Write-Verbose "  SharePoint Protocol:     $($SharePointUrl.Scheme)"
    Write-Verbose "  SharePoint Credential:   $($SharePointCredential.UserName)"
    Set-MIISECMA2Configuration -MAName SPMA -ParameterUse ‘connectivity’ -HTTPProtocol $SharePointUrl.Scheme -HostName $SharePointUrl.Host -Port $SharePointUrl.Port -PictureFlowDirection $PictureFlowDirection -Credentials $SharePointCredential -Verbose

    Write-Verbose "Publishing the Sync Rules Extension DLL to the Sychronization Service extensions folder"      
    Publish-SynchronizationAssembly -Path (Join-Path $Path SynchronizationRulesExtensions.cs) -Verbose
 
    Write-Warning "======================================================================================="
    Write-Warning "IMPORTANT: the SP MA must be opened and closed to refresh the extensible connector"
    Write-Warning "           Use Start-SynchronizationServiceManager to open the Sync Manager tool, then"
    Write-Warning "           ->Management Agents"
    Write-Warning "           ->SPMA (double click)"
    Write-Warning "           ->Click OK three times"
    Write-Warning "======================================================================================="
}##Closing: function Install-SharePointSyncConfiguration

Now we can call this bad boy as before, but with a semi-colon delimited list of OUs to sync with. If we only want to sync with a single OU that’s fine also.

$WorkingPath = "c:\SP16MIMBase"                           # Path to the MA files and Module
$ForestDnsName = "fabrikam.com"                           # DNS name of the Forest
$SyncAccountName = "FABRIKAM\sppsync"                     # Account to use within the SP MA (dirsync rights)
$CentralAdminUrl = "https://spca.fabrikam.com"            # Url of Central Administration
$FarmAdminAccount = "FABRIKAM\Administrator"              # A Farm Administrator (to connect to CA)
$PictureFlow = 'Export only (NEVER from SharePoint)'      # Picture Flow - 'Export only (NEVER from SharePoint)' or 'Import only (ALWAYS from SharePoint)'

# Semi-colon delimited list of DNs of containers to Sync
$OrganizationalUnits = 'OU=Fabrikam Users,DC=fabrikam,DC=com;OU=Ent Users,DC=fabrikam,DC=com;OU=Legal,DC=fabrikam,DC=com'  

# Credential Requests
$SyncAccountCreds = Get-Credential $SyncAccountName
$FarmAccountCreds = Get-Credential $FarmAdminAccount


Import-Module $WorkingPath\SHSharePointSync.psm1 -Force

Install-SharePointSyncConfiguration -Path $WorkingPath -Verbose `
                                    -ForestDnsName $ForestDnsName `
                                    -ForestCredential $SyncAccountCreds `
                                    -SharePointUrl $CentralAdminUrl `
                                    -SharePointCredential $FarmAccountCreds `
                                    -PictureFlowDirection $PictureFlow `
                                    -OrganizationalUnits $OrganizationalUnits 

OK, so what about that downside? Well, sadly due to bugs in the Set-MIISADMAConfiguration cmdlet (or the Management API it’s a wrapper for) we can’t properly select the containers for the configuration partition. In the updated module I include the partition, but no containers within it. This means the entire partition is selected within the AD MA:

2016-08-25_07-10-34

The only container we need here is the CN=Partitions container. You can’t add that to the –Containers parameter because of the bug – you will get a error saying the container doesn’t exist in the partition – thus the only option is to leave it out. That means the entire partition will be selected.

But that’s the only downside, it’s not a very big one. It doesn’t mean we are syncing a bunch of crap to the metaverse. It does mean we are pushing a bunch of extra stuff to the AD connector space thou (705 extra objects in a single domain forest with default schema plus Exchange– basically negligible in that scenario). Of course we can go and clean this up – which needs us to enter the password (!!). It’s a trade off. For customers who are likely to be using this tool as opposed to those who will set it up properly one that’s probably worth it. I can go ahead and execute the Run Profiles and it works just great.

Wait, except for on initial deployment – something that none of the documentation deems it necessary to mention – because the SP MA has a Rules Extension added upon it’s initial configuration it requires a refresh before it will function. If you don’t do this the initial run will result in the following error:

2016-08-25_07-22-31

Simple (age old ILM) trick to sort this is to open the SPMA – double click it, then click OK, click OK again on the Connectivity page, wait on the egg timer, and then click OK once more. That will force a refresh. Now you are good to go.

Of course there’s lots of other things about this toolkit which are “sub optimal” but it’s all about perspective. Regardless of your viewpoint on the removal of UPS, the reality is UPS provided a SharePoint Admin friendly UI for configuring complex multi-domain, multi-forest, import/export scenarios. It’s not the intent of the toolkit to replicate that. It’s intended as a starter solution to get people up and running. For things like additional domains and so forth there is a point of diminishing returns here. Those customers will be doing it “properly” anyway and not faffing about with an import of a lamer default MA configuration. Similarly those doing it properly will be updating the configuration when they move their tested MA configs through Dev – Test – UAT – Production. (Yes, really. People do this!). And that’s without even getting into the whole “Classic Provisioning” thing. So bottom line is they won’t be using this toolkit.

However for those customers looking for a quick and dirty way to get up and running, or for SharePoint practitioners wanting to get started with MIM, these tweaks improve things in the two areas they most complain about.

Another tip is for those that have got their panties in a wad over things like UserAccountControl and so on. Do it on a dev box, and export your MA. You can then use the toolkit to move it into a fresh setup.  You don’t have to use the sample MA configurations provided.

Before I go I have to address another common question regarding the github, “why aren’t you doing pull requests on this stuff?”. Well it’s a very long story but the short and sweet is that I have zero interest in doing free work for Microsoft which masquerades as “open source” especially when there is no actual commitment or leadership. I especially don’t wish to do it when the contribution workflow is hopeless and the publication mechanism is at odds with the standard Windows PowerShell approach. If it’s not going to be done right…. you get the idea.

 

s.