DSCResources/MSFT_SPUserProfileSyncConnection/MSFT_SPUserProfileSyncConnection.psm1

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Forest,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $ConnectionCredentials,

        [Parameter(Mandatory = $true)]
        [System.String]
        $UserProfileService,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $IncludedOUs,

        [Parameter()]
        [System.String[]]
        $ExcludedOUs,

        [Parameter()]
        [System.String]
        $Server,

        [Parameter()]
        [System.Boolean]
        $Force,

        [Parameter()]
        [System.Boolean]
        $UseSSL,

        [Parameter()]
        [ValidateSet("ActiveDirectory","BusinessDataCatalog")]
        [System.String]
        $ConnectionType,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $InstallAccount
    )

    Write-Verbose -Message "Getting user profile service sync connection $Name"

    $result = Invoke-SPDSCCommand -Credential $InstallAccount `
                                  -Arguments $PSBoundParameters `
                                  -ScriptBlock {
        $params = $args[0]

        $ups = Get-SPServiceApplication -Name $params.UserProfileService `
                                        -ErrorAction SilentlyContinue

        if ($null -eq $ups)
        {
            return $null
        }
        else
        {
            $context = Get-SPDSCServiceContext -ProxyGroup $ups.ServiceApplicationProxyGroup
            $upcm = New-Object -TypeName "Microsoft.Office.Server.UserProfiles.UserProfileConfigManager" `
                               -ArgumentList $context

            $connection = $upcm.ConnectionManager | Where-Object -FilterScript {
                $_.DisplayName -eq $params.Name
            }
            if ($null -eq $connection)
            {
                return $null
            }
            $namingContext = $connection.NamingContexts | Select-Object -First 1
            if ($null -eq $namingContext)
            {
                $BINDING_FLAGS = ([System.Reflection.BindingFlags]::NonPublic -bOr [System.Reflection.BindingFlags]::Instance)
                $adImportNamespace = [Microsoft.Office.Server.UserProfiles.ActiveDirectoryImportConnection]
                $METHOD_GET_NAMINGCONTEXTS = $adImportNamespace::GetMethod("get_NamingContexts", $BINDING_FLAGS)
                $METHOD_GET_ACCOUNTUSERNAME = $adImportNamespace::GetMethod("get_AccountUsername", $BINDING_FLAGS)
                $METHOD_GET_ACCOUNTDOMAIN = $adImportNamespace::GetMethod("get_AccountDomain", $BINDING_FLAGS)
                $METHOD_GET_USESSL = $adImportNamespace::GetMethod("get_UseSSL", $BINDING_FLAGS)
                $namingContexts = $METHOD_GET_NAMINGCONTEXTS.Invoke($connection, $null)
                $accountName = $METHOD_GET_ACCOUNTUSERNAME.Invoke($connection, $null)
                $accountDomain = $METHOD_GET_ACCOUNTDOMAIN.Invoke($connection, $null)
                $accountCredentials = $accountDomain + "\" + $accountName
                $useSSL = $METHOD_GET_USESSL.Invoke($connection, $null)

                if($null -eq $namingContexts)
                {
                    return $null
                }

                return @{
                    UserprofileService = $UserProfileService
                    Forest = $namingContexts.DistinguishedName
                    Credentials = $accountCredentials
                    IncludedOUs = $namingContext.ContainersIncluded
                    ExcludedOUs = $namingContext.ContainersExcluded
                    Server = $null
                    UseSSL = $useSSL
                    ConnectionType = $conn.Type
                    Force = $params.Force
                }
            }
            $accountCredentials = "$($connection.AccountDomain)\$($connection.AccountUsername)"
            $domainController = $namingContext.PreferredDomainControllers | Select-Object -First 1
            return @{
                UserProfileService = $UserProfileService
                Forest = $connection.Server
                Name = $namingContext.DisplayName
                Credentials = $accountCredentials
                IncludedOUs = $namingContext.ContainersIncluded
                ExcludedOUs = $namingContext.ContainersExcluded
                Server =$domainController
                UseSSL = $connection.UseSSL
                ConnectionType = $connection.Type.ToString()
                Force = $params.Force
            }
        }
    }
    return $result
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Forest,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $ConnectionCredentials,

        [Parameter(Mandatory = $true)]
        [System.String]
        $UserProfileService,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $IncludedOUs,

        [Parameter()]
        [System.String[]]
        $ExcludedOUs,

        [Parameter()]
        [System.String]
        $Server,

        [Parameter()]
        [System.Boolean]
        $Force,

        [Parameter()]
        [System.Boolean]
        $UseSSL,

        [Parameter()]
        [ValidateSet("ActiveDirectory","BusinessDataCatalog")]
        [System.String]
        $ConnectionType,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $InstallAccount
   )

    Write-Verbose -Message "Setting user profile service sync connection $Name"

    Invoke-SPDSCCommand -Credential $InstallAccount `
                        -Arguments @($PSBoundParameters, $PSScriptRoot) `
                        -ScriptBlock {

        $params = $args[0]
        $scriptRoot = $args[1]

        Import-Module -Name (Join-Path $scriptRoot "MSFT_SPUserProfileSyncConnection.psm1")

        if ($params.ContainsKey("InstallAccount"))
        {
            $params.Remove("InstallAccount") | Out-Null
        }
        $ups = Get-SPServiceApplication -Name $params.UserProfileService -ErrorAction SilentlyContinue

        if ($null -eq $ups)
        {
            throw "User Profile Service Application $($params.UserProfileService) not found"
        }
        $context = Get-SPDSCServiceContext -ProxyGroup $ups.ServiceApplicationProxyGroup

        Write-Verbose -Message "retrieving UserProfileConfigManager "
        $upcm = New-Object -TypeName "Microsoft.Office.Server.UserProfiles.UserProfileConfigManager" `
                           -ArgumentList @($context)

        if ($upcm.IsSynchronizationRunning())
        {
            throw "Synchronization is in Progress."
        }

        $connection = $upcm.ConnectionManager | Where-Object -FilterScript {
            $_.DisplayName -eq $params.Name
        } | Select-Object -first 1

        if ($null -ne $connection -and $params.Forest -ieq  $connection.Server)
        {
            $domain = $params.ConnectionCredentials.UserName.Split("\")[0]
            $userName= $params.ConnectionCredentials.UserName.Split("\")[1]
            $connection.SetCredentials($domain, $userName, $params.ConnectionCredentials.Password)

            $connection.NamingContexts | ForEach-Object -Process {
                $namingContext = $_
                if ($params.ContainsKey("IncludedOUs"))
                {
                    $namingContext.ContainersIncluded.Clear()
                    $params.IncludedOUs| ForEach-Object -Process {
                        $namingContext.ContainersIncluded.Add($_)
                    }
                }
                $namingContext.ContainersExcluded.Clear()
                if ($params.ContainsKey("ExcludedOUs"))
                {
                    $params.IncludedOUs| ForEach-Object -Process {
                        $namingContext.ContainersExcluded.Add($_)
                    }
                }
            }
            $connection.Update()
            $connection.RefreshSchema($params.ConnectionCredentials.Password)

            return
        }
        else
        {
            Write-Verbose -Message "creating a new connection "
            if ($null -ne $connection -and $params.Forest -ine  $connection.Server)
            {
                if ($params.ContainsKey("Force") -and $params.Force -eq $true)
                {
                    $connection.Delete()
                }
                else
                {
                    throw "connection exists and forest is different. use force"
                }

            }

            $servers = New-Object -TypeName "System.Collections.Generic.List[[System.String]]"
            if ($params.ContainsKey("Server"))
            {
                $servers.add($params.Server)
            }
            $listIncludedOUs = New-Object -TypeName "System.Collections.Generic.List[[System.String]]"
            $params.IncludedOUs | ForEach-Object -Process {
                $listIncludedOUs.Add($_)
            }

            $listExcludedOUs = New-Object -TypeName "System.Collections.Generic.List[[System.String]]"
            if ($params.ContainsKey("ExcludedOus"))
            {
                $params.ExcludedOus | ForEach-Object -Process {
                    $listExcludedOUs.Add($_)
                }
            }
            $list = New-Object -TypeName System.Collections.Generic.List[[Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext]]

            $partition = Get-SPDSCADSIObject -LdapPath ("LDAP://" +("DC=" + $params.Forest.Replace(".", ",DC=")))
            $list.Add((New-Object -TypeName "Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext" `
                                  -ArgumentList @(
                                            $partition.distinguishedName,
                                            $params.Forest,
                                            $false,
                                            (New-Object -TypeName "System.Guid" `
                                                        -ArgumentList $partition.objectGUID),
                                            $listIncludedOUs,
                                            $listExcludedOUs,
                                            $null ,
                                            $false)))
            $partition = Get-SPDSCADSIObject -LdapPath ("LDAP://CN=Configuration," + ("DC=" + $params.Forest.Replace(".", ",DC=")))
            $list.Add((New-Object -TypeName "Microsoft.Office.Server.UserProfiles.DirectoryServiceNamingContext" `
                                  -ArgumentList @(
                                            $partition.distinguishedName,
                                            $params.Forest,
                                            $true,
                                            (New-Object -TypeName "System.Guid" `
                                                        -ArgumentList $partition.objectGUID),
                                            $listIncludedOUs ,
                                            $listExcludedOUs ,
                                            $null ,
                                            $false)))

            $userDomain = $params.ConnectionCredentials.UserName.Split("\")[0]
            $userName= $params.ConnectionCredentials.UserName.Split("\")[1]

            $installedVersion = Get-SPDSCInstalledProductVersion

            switch($installedVersion.FileMajorPart)
            {
                15{
                    $upcm.ConnectionManager.AddActiveDirectoryConnection( [Microsoft.Office.Server.UserProfiles.ConnectionType]::ActiveDirectory,  `
                                            $params.Name, `
                                            $params.Forest, `
                                            $params.UseSSL, `
                                            $userDomain, `
                                            $userName, `
                                            $params.ConnectionCredentials.Password, `
                                            $list, `
                                            $null,`
                                            $null) | Out-Null
               }
               16
               {
                   Add-SPProfileSyncConnection -ProfileServiceApplication $ups -ConnectionForestName $params.Forest -ConnectionDomain $userDomain `
                       -ConnectionUserName $userName -ConnectionPassword $params.ConnectionCredentials.Password -ConnectionUseSSL $params.UseSSL `
                       -ConnectionSynchronizationOU $params.IncludedOUs.ToString()
               }
            }
        }
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Forest,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $ConnectionCredentials,

        [Parameter(Mandatory = $true)]
        [System.String]
        $UserProfileService,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $IncludedOUs,

        [Parameter()]
        [System.String[]]
        $ExcludedOUs,

        [Parameter()]
        [System.String]
        $Server,

        [Parameter()]
        [System.Boolean]
        $Force,

        [Parameter()]
        [System.Boolean]
        $UseSSL,

        [Parameter()]
        [ValidateSet("ActiveDirectory","BusinessDataCatalog")]
        [System.String]
        $ConnectionType,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $InstallAccount
    )

    Write-Verbose -Message "Testing for user profile service sync connection $Name"

    $CurrentValues = Get-TargetResource @PSBoundParameters

    if ($null -eq $CurrentValues)
    {
        return $false
    }

    if ($Force -eq $true)
    {
        return $false
    }

    return Test-SPDscParameterState -CurrentValues $CurrentValues `
                                    -DesiredValues $PSBoundParameters `
                                    -ValuesToCheck @("Name",
                                                     "Forest",
                                                     "UserProfileService",
                                                     "Server",
                                                     "UseSSL",
                                                     "IncludedOUs",
                                                     "ExcludedOUs")
}

<#
.DESCRIPTION
 
This method is not intensed for public use, and was created to facilitate unit testing
#>

function Get-SPDSCADSIObject
{
    param(
        [Parameter()]
        [string] $LdapPath
    )
    return [ADSI]($LdapPath)
}

Export-ModuleMember -Function *-TargetResource, Get-SPDSCADSIObject