DSCResources/MSFT_SPWebAppPeoplePickerSettings/MSFT_SPWebAppPeoplePickerSettings.psm1

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

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

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

        [Parameter()]
        [System.UInt16]
        $ActiveDirectorySearchTimeout,

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

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

        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]
        $SearchActiveDirectoryDomains
    )

    Write-Verbose -Message "Getting People Picker Settings for $WebAppUrl"

    $result = Invoke-SPDscCommand -Arguments $PSBoundParameters `
        -ScriptBlock {
        $params = $args[0]

        $wa = Get-SPWebApplication -Identity $params.WebAppUrl `
            -ErrorAction SilentlyContinue

        if ($null -eq $wa)
        {
            return @{
                WebAppUrl                      = $params.WebAppUrl
                ActiveDirectoryCustomFilter    = $null
                ActiveDirectoryCustomQuery     = $null
                ActiveDirectorySearchTimeout   = $null
                OnlySearchWithinSiteCollection = $null
                SearchActiveDirectoryDomains   = $null
            }
        }

        $searchADDomains = @()
        foreach ($searchDomain in $wa.PeoplePickerSettings.SearchActiveDirectoryDomains)
        {
            $searchADDomain = @{ }
            $searchADDomain.FQDN = $searchDomain.DomainName
            $searchADDomain.IsForest = $searchDomain.IsForest
            $searchADDomain.AccessAccount = $searchDomain.LoginName
            $searchADDomain.CustomFilter = $searchDomain.CustomFilter
            $searchADDomain.ShortDomainName = $searchDomain.ShortDomainName
            $searchADDomains += $searchADDomain
        }

        return @{
            WebAppUrl                                   = $params.WebAppUrl
            ActiveDirectoryCustomFilter                 = $wa.PeoplePickerSettings.ActiveDirectoryCustomFilter
            ActiveDirectoryCustomQuery                  = $wa.PeoplePickerSettings.ActiveDirectoryCustomQuery
            ActiveDirectorySearchTimeout                = $wa.PeoplePickerSettings.ActiveDirectorySearchTimeout.TotalSeconds
            OnlySearchWithinSiteCollection              = $wa.PeoplePickerSettings.OnlySearchWithinSiteCollection
            PeopleEditorOnlyResolveWithinSiteCollection = $wa.PeoplePickerSettings.PeopleEditorOnlyResolveWithinSiteCollection
            SearchActiveDirectoryDomains                = $searchADDomains
        }
    }
    return $result
}

function Set-TargetResource
{
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification = "Ignoring this because the used AccessAccount does not use SecureString to handle the password")]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $WebAppUrl,

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

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

        [Parameter()]
        [System.UInt16]
        $ActiveDirectorySearchTimeout,

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

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

        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]
        $SearchActiveDirectoryDomains
    )

    Write-Verbose -Message "Setting People Picker Settings for $WebAppUrl"

    ## Perform changes
    Invoke-SPDscCommand -Arguments @($PSBoundParameters, $MyInvocation.MyCommand.Source) `
        -ScriptBlock {
        $params = $args[0]
        $eventSource = $args[1]

        $wa = Get-SPWebApplication -Identity $params.WebAppUrl -ErrorAction SilentlyContinue

        if ($null -eq $wa)
        {
            $message = "Specified web application could not be found."
            Add-SPDscEvent -Message $message `
                -EntryType 'Error' `
                -EventID 100 `
                -Source $eventSource
            throw $message
        }

        if ($params.ContainsKey("ActiveDirectoryCustomFilter"))
        {
            if ($params.ActiveDirectoryCustomFilter -ne $wa.PeoplePickerSettings.ActiveDirectoryCustomFilter)
            {
                $wa.PeoplePickerSettings.ActiveDirectoryCustomFilter = $params.ActiveDirectoryCustomFilter
            }
        }

        if ($params.ContainsKey("ActiveDirectoryCustomQuery"))
        {
            if ($params.ActiveDirectoryCustomQuery -ne $wa.PeoplePickerSettings.ActiveDirectoryCustomQuery)
            {
                $wa.PeoplePickerSettings.ActiveDirectoryCustomQuery = $params.ActiveDirectoryCustomQuery
            }
        }

        if ($params.ContainsKey("ActiveDirectorySearchTimeout"))
        {
            if ($params.ActiveDirectorySearchTimeout -ne $wa.PeoplePickerSettings.ActiveDirectorySearchTimeout.TotalSeconds)
            {
                $wa.PeoplePickerSettings.ActiveDirectorySearchTimeout = New-TimeSpan -Seconds $params.ActiveDirectorySearchTimeout
            }
        }

        if ($params.ContainsKey("OnlySearchWithinSiteCollection"))
        {
            if ($params.OnlySearchWithinSiteCollection -ne $wa.PeoplePickerSettings.OnlySearchWithinSiteCollection)
            {
                $wa.PeoplePickerSettings.OnlySearchWithinSiteCollection = $params.OnlySearchWithinSiteCollection
            }
        }

        if ($params.ContainsKey("PeopleEditorOnlyResolveWithinSiteCollection"))
        {
            if ($params.PeopleEditorOnlyResolveWithinSiteCollection -ne $wa.PeoplePickerSettings.PeopleEditorOnlyResolveWithinSiteCollection)
            {
                $wa.PeoplePickerSettings.PeopleEditorOnlyResolveWithinSiteCollection = $params.PeopleEditorOnlyResolveWithinSiteCollection
            }
        }

        if ($params.ContainsKey("SearchActiveDirectoryDomains"))
        {
            foreach ($searchADDomain in $params.SearchActiveDirectoryDomains)
            {
                $configuredDomain = $wa.PeoplePickerSettings.SearchActiveDirectoryDomains | `
                        Where-Object -FilterScript {
                        $_.DomainName -eq $searchADDomain.FQDN -and `
                            $_.IsForest -eq $searchADDomain.IsForest
                    }
                if ($null -eq $configuredDomain)
                {
                    # Add domain
                    $adsearchobj = New-Object -TypeName Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
                    $adsearchobj.DomainName = $searchADDomain.FQDN

                    $prop = $searchADDomain.CimInstanceProperties | Where-Object -FilterScript {
                        $_.Name -eq "NetBIOSName"
                    }
                    if ($null -ne $prop)
                    {
                        $adsearchobj.ShortDomainName = $searchADDomain.NetBIOSName
                    }

                    $adsearchobj.IsForest = $searchADDomain.IsForest

                    if ($null -ne $searchADDomain.AccessAccount)
                    {
                        $adsearchobj.LoginName = $searchADDomain.AccessAccount.UserName

                        if ([string]::IsNullOrEmpty($searchADDomain.AccessAccount.Password))
                        {
                            $adsearchobj.SetPassword($null)
                        }
                        else
                        {
                            $accessAccountPassword = ConvertTo-SecureString $searchADDomain.AccessAccount.Password -AsPlainText -Force
                            $adsearchobj.SetPassword($accessAccountPassword)
                        }
                    }
                    if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'CustomFilter' }))
                    {
                        $adsearchobj.CustomFilter = $searchADDomain.CustomFilter
                    }
                    if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'ShortDomainName' }))
                    {
                        $adsearchobj.ShortDomainName = $searchADDomain.ShortDomainName
                    }

                    $wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj)
                }
                else
                {
                    if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'AccessAccount' }))
                    {
                        $configuredDomain.LoginName = $searchADDomain.AccessAccount.UserName

                        if ([string]::IsNullOrEmpty($searchADDomain.AccessAccount.Password))
                        {
                            $configuredDomain.SetPassword($null)
                        }
                        else
                        {
                            $accessAccountPassword = ConvertTo-SecureString $searchADDomain.AccessAccount.Password -AsPlainText -Force
                            $configuredDomain.SetPassword($accessAccountPassword)
                        }
                    }
                    if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'CustomFilter' }))
                    {
                        $configuredDomain.CustomFilter = $searchADDomain.CustomFilter
                    }
                    if ($null -ne ($searchADDomain.CimInstanceProperties | Where-Object -FilterScript { $_.Name -eq 'ShortDomainName' }))
                    {
                        $configuredDomain.ShortDomainName = $searchADDomain.ShortDomainName
                    }
                }
            }

            # Reverse Check: Configured domains do not exist in config
            $removeDomains = @()
            foreach ($waSearchADDomain in $wa.PeoplePickerSettings.SearchActiveDirectoryDomains)
            {
                $specifiedDomain = $params.SearchActiveDirectoryDomains | Where-Object -FilterScript {
                    $_.FQDN -eq $waSearchADDomain.DomainName -and `
                        $_.IsForest -eq $waSearchADDomain.IsForest
                }

                if ($null -eq $specifiedDomain)
                {
                    # Configured domain not found in DSC configuration, removing domain
                    $removeDomains += $waSearchADDomain
                }
            }

            foreach ($domain in $removeDomains)
            {
                $wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Remove($domain) | Out-Null
            }
        }
        $wa.Update()
    }
}

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

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

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

        [Parameter()]
        [System.UInt16]
        $ActiveDirectorySearchTimeout,

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

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

        [Parameter()]
        [Microsoft.Management.Infrastructure.CimInstance[]]
        $SearchActiveDirectoryDomains
    )

    Write-Verbose -Message "Testing People Picker Settings for $WebAppUrl"

    $CurrentValues = Get-TargetResource @PSBoundParameters

    Write-Verbose -Message "Current Values: $(Convert-SPDscHashtableToString -Hashtable $CurrentValues)"
    Write-Verbose -Message "Target Values: $(Convert-SPDscHashtableToString -Hashtable $PSBoundParameters)"

    # Testing SearchActiveDirectoryDomains against configured values
    foreach ($searchADDomain in $SearchActiveDirectoryDomains)
    {
        $configuredDomain = $CurrentValues.SearchActiveDirectoryDomains | `
                Where-Object -FilterScript {
                $_.FQDN -eq $searchADDomain.FQDN -and `
                    $_.IsForest -eq $searchADDomain.IsForest
            }
        if ($null -eq $configuredDomain)
        {
            $message = "Current SearchActiveDirectoryDomains does not match the desired state."
            Write-Verbose -Message $message
            Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source

            Write-Verbose -Message "Test-TargetResource returned false"
            return $false
        }
    }

    # Reverse: Testing configured values against SearchActiveDirectoryDomains
    foreach ($searchADDomain in $CurrentValues.SearchActiveDirectoryDomains)
    {
        $specifiedDomain = $SearchActiveDirectoryDomains | Where-Object -FilterScript {
            $_.FQDN -eq $searchADDomain.FQDN -and `
                $_.IsForest -eq $searchADDomain.IsForest
        }

        if ($null -eq $specifiedDomain)
        {
            $message = "Current SearchActiveDirectoryDomains does not match the desired state."
            Write-Verbose -Message $message
            Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source

            Write-Verbose -Message "Test-TargetResource returned false"
            return $false
        }
        else
        {
            if ($searchADDomain.ContainsKey('AccessAccount') -and $searchADDomain.AccessAccount.UserName -ne $specifiedDomain.LoginName)
            {
                $message = "Current LoginName Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state."
                Write-Verbose -Message $message
                Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source

                Write-Verbose -Message "Test-TargetResource returned false"
                return $false
            }
            if ($searchADDomain.ContainsKey('CustomFilter') -and $searchADDomain.CustomFilter -ne $specifiedDomain.CustomFilter)
            {
                $message = "Current CustomFilter Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state."
                Write-Verbose -Message $message
                Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source

                Write-Verbose -Message "Test-TargetResource returned false"
                return $false
            }
            if ($searchADDomain.ContainsKey('ShortDomainName') -and $searchADDomain.ShortDomainName -ne $specifiedDomain.ShortDomainName)
            {
                $message = "Current ShortDomainName Property of SearchActiveDirectoryDomain $searchADDomain does not match the desired state."
                Write-Verbose -Message $message
                Add-SPDscEvent -Message $message -EntryType 'Error' -EventID 1 -Source $MyInvocation.MyCommand.Source

                Write-Verbose -Message "Test-TargetResource returned false"
                return $false
            }
        }
    }

    $result = Test-SPDscParameterState -CurrentValues $CurrentValues `
        -Source $($MyInvocation.MyCommand.Source) `
        -DesiredValues $PSBoundParameters `
        -ValuesToCheck @("ActiveDirectoryCustomFilter",
        "ActiveDirectoryCustomQuery",
        "ActiveDirectorySearchTimeout",
        "OnlySearchWithinSiteCollection",
        "PeopleEditorOnlyResolveWithinSiteCollection")

    Write-Verbose -Message "Test-TargetResource returned $result"

    return $result
}

Export-ModuleMember -Function *-TargetResource