DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1

$script:sqlServerDscHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\SqlServerDsc.Common'
$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common'

Import-Module -Name $script:sqlServerDscHelperModulePath
Import-Module -Name $script:resourceHelperModulePath

$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'

<#
    .SYNOPSIS
        Gets the specified Availability Group Replica from the specified Availability Group.
 
    .PARAMETER Name
        The name of the availability group replica. For named instances this
        must be in the following format ServerName\InstanceName.
 
    .PARAMETER AvailabilityGroupName
        The name of the availability group.
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
#>

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

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

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

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

    Write-Verbose -Message (
        $script:localizedData.GetAvailabilityGroup -f $Name, $AvailabilityGroupName, $InstanceName
    )

    # Connect to the instance
    $serverObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName -ErrorAction 'Stop'

    # Define current version for check compatibility
    $sqlMajorVersion = $serverObject.Version.Major

    # Is this node actively hosting the SQL instance?
    $isActiveNode = Test-ActiveNode -ServerObject $serverObject

    # Get the endpoint properties
    $endpoint = $serverObject.Endpoints | Where-Object -FilterScript { $_.EndpointType -eq 'DatabaseMirroring' }
    if ( $endpoint )
    {
        $endpointPort = $endpoint.Protocol.Tcp.ListenerPort
    }

    # Create the return object
    $alwaysOnAvailabilityGroupReplicaResource = @{
        Ensure                        = 'Absent'
        Name                          = $Name
        AvailabilityGroupName         = $AvailabilityGroupName
        AvailabilityMode              = ''
        BackupPriority                = ''
        ConnectionModeInPrimaryRole   = ''
        ConnectionModeInSecondaryRole = ''
        FailoverMode                  = ''
        EndpointUrl                   = ''
        IsActiveNode                  = $isActiveNode
        ReadOnlyRoutingConnectionUrl  = ''
        ReadOnlyRoutingList           = @()
        ServerName                    = $ServerName
        InstanceName                  = $InstanceName
        EndpointPort                  = $endpointPort
        EndpointHostName              = $serverObject.NetName
    }

    if ( ( $sqlMajorVersion -ge 13 ) )
    {
        $alwaysOnAvailabilityGroupReplicaResource.Add('SeedingMode', '')
    }

    # Get the availability group
    $availabilityGroup = $serverObject.AvailabilityGroups[$AvailabilityGroupName]

    if ( $availabilityGroup )
    {
        # Try to find the replica
        $availabilityGroupReplica = $availabilityGroup.AvailabilityReplicas[$Name]

        if ( $availabilityGroupReplica )
        {
            # Add the Availability Group Replica properties to the results
            $alwaysOnAvailabilityGroupReplicaResource.Ensure = 'Present'
            $alwaysOnAvailabilityGroupReplicaResource.AvailabilityMode = $availabilityGroupReplica.AvailabilityMode
            $alwaysOnAvailabilityGroupReplicaResource.BackupPriority = $availabilityGroupReplica.BackupPriority
            $alwaysOnAvailabilityGroupReplicaResource.ConnectionModeInPrimaryRole = $availabilityGroupReplica.ConnectionModeInPrimaryRole
            $alwaysOnAvailabilityGroupReplicaResource.ConnectionModeInSecondaryRole = $availabilityGroupReplica.ConnectionModeInSecondaryRole
            $alwaysOnAvailabilityGroupReplicaResource.FailoverMode = $availabilityGroupReplica.FailoverMode
            $alwaysOnAvailabilityGroupReplicaResource.EndpointUrl = $availabilityGroupReplica.EndpointUrl
            $alwaysOnAvailabilityGroupReplicaResource.ReadOnlyRoutingConnectionUrl = $availabilityGroupReplica.ReadOnlyRoutingConnectionUrl
            $alwaysOnAvailabilityGroupReplicaResource.ReadOnlyRoutingList = $availabilityGroupReplica.ReadOnlyRoutingList

            if (  $sqlMajorVersion -ge 13  )
            {
                if ( (Get-Command -Name 'New-SqlAvailabilityReplica').Parameters.ContainsKey('SeedingMode') )
                {
                    $alwaysOnAvailabilityGroupReplicaResource.'SeedingMode' = $availabilityGroupReplica.SeedingMode
                }
                else
                {
                    $alwaysOnAvailabilityGroupReplicaResource.'SeedingMode' = $null
                }
            }
        }
    }

    return $alwaysOnAvailabilityGroupReplicaResource
}

<#
    .SYNOPSIS
        Creates or removes the availability group replica in accordance with the desired state.
 
    .PARAMETER Name
        The name of the availability group replica. For named instances this
        must be in the following format ServerName\InstanceName.
 
    .PARAMETER AvailabilityGroupName
        The name of the availability group.
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
 
    .PARAMETER PrimaryReplicaServerName
        Hostname of the SQL Server where the primary replica is expected to be active. If the primary replica is not found here, the resource will attempt to find the host that holds the primary replica and connect to it.
 
    .PARAMETER PrimaryReplicaInstanceName
        Name of the SQL instance where the primary replica lives.
 
    .PARAMETER Ensure
        Specifies if the availability group should be present or absent. Default is Present.
 
    .PARAMETER AvailabilityMode
        Specifies the replica availability mode. Default when creating a replica is 'AsynchronousCommit'.
 
    .PARAMETER BackupPriority
        Specifies the desired priority of the replicas in performing backups. The acceptable values for this parameter are integers from 0 through 100. Of the set of replicas which are online and available, the replica that has the highest priority performs the backup. When creating a replica the default is 50.
 
    .PARAMETER ConnectionModeInPrimaryRole
        Specifies how the availability replica handles connections when in the primary role.
 
    .PARAMETER ConnectionModeInSecondaryRole
        Specifies how the availability replica handles connections when in the secondary role.
 
    .PARAMETER EndpointHostName
        Specifies the hostname or IP address of the availability group replica endpoint. When creating a group the default is the instance network name which is set in the code because the value can only be determined when connected to the SQL Instance.
 
    .PARAMETER FailoverMode
        Specifies the failover mode. When creating a replica the default is 'Manual'.
 
    .PARAMETER ReadOnlyRoutingConnectionUrl
        Specifies the fully-qualified domain name (FQDN) and port to use when routing to the replica for read only connections.
 
    .PARAMETER ReadOnlyRoutingList
        Specifies an ordered list of replica server names that represent the probe sequence for connection director to use when redirecting read-only connections through this availability replica. This parameter applies if the availability replica is the current primary replica of the availability group.
 
    .PARAMETER ProcessOnlyOnActiveNode
        Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.
        Not used in Set-TargetResource.
 
    .PARAMETER SeedingMode
        Specifies the seeding mode. When creating a replica the default is 'Manual'.
        This parameter can only be used when the module SqlServer is installed.
#>

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

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

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

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

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

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

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [ValidateSet('AsynchronousCommit', 'SynchronousCommit')]
        [System.String]
        $AvailabilityMode,

        [Parameter()]
        [ValidateRange(0, 100)]
        [System.UInt32]
        $BackupPriority,

        [Parameter()]
        [ValidateSet('AllowAllConnections', 'AllowReadWriteConnections')]
        [System.String]
        $ConnectionModeInPrimaryRole,

        [Parameter()]
        [ValidateSet('AllowNoConnections', 'AllowReadIntentConnectionsOnly', 'AllowAllConnections')]
        [System.String]
        $ConnectionModeInSecondaryRole,

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

        [Parameter()]
        [ValidateSet('Automatic', 'Manual')]
        [System.String]
        $FailoverMode,

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

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

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

        [Parameter()]
        [ValidateSet('Automatic', 'Manual')]
        [System.String]
        $SeedingMode = 'Manual'
    )

    Import-SqlDscPreferredModule

    # Connect to the instance
    $serverObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName -ErrorAction 'Stop'

    # Define current version for check compatibility
    $sqlMajorVersion = $serverObject.Version.Major

    # Determine if HADR is enabled on the instance. If not, throw an error
    if ( -not $serverObject.IsHadrEnabled )
    {
        $errorMessage = $script:localizedData.HadrNotEnabled
        New-InvalidOperationException -Message $errorMessage
    }

    # Get the Availability Group if it exists
    $availabilityGroup = $serverObject.AvailabilityGroups[$AvailabilityGroupName]

    # Make sure we're communicating with the primary replica in order to make changes to the replica
    if ( $availabilityGroup )
    {
        while ( $availabilityGroup.LocalReplicaRole -ne 'Primary' )
        {
            $primaryServerObject = Get-PrimaryReplicaServerObject -ServerObject $serverObject -AvailabilityGroup $availabilityGroup
            $availabilityGroup = $primaryServerObject.AvailabilityGroups[$AvailabilityGroupName]
        }
    }

    switch ( $Ensure )
    {
        'Absent'
        {
            if ( $availabilityGroup )
            {
                $availabilityGroupReplica = $availabilityGroup.AvailabilityReplicas[$Name]

                if ( $availabilityGroupReplica )
                {
                    try
                    {
                        Write-Verbose -Message (
                            $script:localizedData.RemoveAvailabilityReplica -f $Name, $AvailabilityGroupName, $InstanceName
                        )

                        Remove-SqlAvailabilityReplica -InputObject $availabilityGroupReplica -Confirm:$false -ErrorAction Stop
                    }
                    catch
                    {
                        $errorMessage = $script:localizedData.FailedRemoveAvailabilityGroupReplica -f $Name, $AvailabilityGroupName, $InstanceName
                        New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
                    }
                }
            }
        }

        'Present'
        {
            # Ensure the appropriate cluster permissions are present
            Test-ClusterPermissions -ServerObject $serverObject

            # Make sure a database mirroring endpoint exists.
            $endpoint = $serverObject.Endpoints | Where-Object -FilterScript { $_.EndpointType -eq 'DatabaseMirroring' }
            if ( -not $endpoint )
            {
                $errorMessage = $script:localizedData.DatabaseMirroringEndpointNotFound -f ('{0}\{1}' -f $ServerName, $InstanceName)
                New-ObjectNotFoundException -Message $errorMessage
            }

            # If a hostname for the endpoint was not specified, define it now.
            if ( -not $EndpointHostName )
            {
                $EndpointHostName = $serverObject.NetName
            }

            # Get the endpoint port
            $endpointPort = $endpoint.Protocol.Tcp.ListenerPort

            # Determine if the Availability Group exists on the instance
            if ( $availabilityGroup )
            {
                # Make sure the replica exists on the instance. If the availability group exists, the replica should exist.
                $availabilityGroupReplica = $availabilityGroup.AvailabilityReplicas[$Name]
                if ( $availabilityGroupReplica )
                {
                    $availabilityGroupReplicaUpdatesRequired = $false

                    # Get the parameters that were submitted to the function
                    [System.Array] $submittedParameters = $PSBoundParameters.Keys

                    if ( ( $submittedParameters -contains 'FailoverMode' ) -and ( $FailoverMode -ne $availabilityGroupReplica.FailoverMode ) )
                    {
                        $availabilityGroupReplica.FailoverMode = $FailoverMode
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'AvailabilityMode' ) -and (  $AvailabilityMode -ne $availabilityGroupReplica.AvailabilityMode ) )
                    {
                        $availabilityGroupReplica.AvailabilityMode = $AvailabilityMode
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'BackupPriority' ) -and ( $BackupPriority -ne $availabilityGroupReplica.BackupPriority ) )
                    {
                        $availabilityGroupReplica.BackupPriority = $BackupPriority
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    # Make sure ConnectionModeInPrimaryRole has a value in order to avoid false positive matches when the parameter is not defined
                    if ( ( -not [System.String]::IsNullOrEmpty($ConnectionModeInPrimaryRole) ) -and ( $ConnectionModeInPrimaryRole -ne $availabilityGroupReplica.ConnectionModeInPrimaryRole ) )
                    {
                        $availabilityGroupReplica.ConnectionModeInPrimaryRole = $ConnectionModeInPrimaryRole
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    # Make sure ConnectionModeInSecondaryRole has a value in order to avoid false positive matches when the parameter is not defined
                    if ( ( -not [System.String]::IsNullOrEmpty($ConnectionModeInSecondaryRole) ) -and ( $ConnectionModeInSecondaryRole -ne $availabilityGroupReplica.ConnectionModeInSecondaryRole ) )
                    {
                        $availabilityGroupReplica.ConnectionModeInSecondaryRole = $ConnectionModeInSecondaryRole
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    # Break out the EndpointUrl properties
                    $currentEndpointProtocol, $currentEndpointHostName, $currentEndpointPort = $availabilityGroupReplica.EndpointUrl.Replace('//', '').Split(':')

                    if ( $endpoint.Protocol.Tcp.ListenerPort -ne $currentEndpointPort )
                    {
                        $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointPort, $endpoint.Protocol.Tcp.ListenerPort)
                        $availabilityGroupReplica.EndpointUrl = $newEndpointUrl
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'EndpointHostName' ) -and ( $EndpointHostName -ne $currentEndpointHostName ) )
                    {
                        $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointHostName, $EndpointHostName)
                        $availabilityGroupReplica.EndpointUrl = $newEndpointUrl
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( $currentEndpointProtocol -ne 'TCP' )
                    {
                        $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointProtocol, 'TCP')
                        $availabilityGroupReplica.EndpointUrl = $newEndpointUrl
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'ReadOnlyRoutingConnectionUrl' ) -and ( $ReadOnlyRoutingConnectionUrl -ne $availabilityGroupReplica.ReadOnlyRoutingConnectionUrl ) )
                    {
                        $availabilityGroupReplica.ReadOnlyRoutingConnectionUrl = $ReadOnlyRoutingConnectionUrl
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'ReadOnlyRoutingList' ) -and ( ( $ReadOnlyRoutingList -join ',' ) -ne ( $availabilityGroupReplica.ReadOnlyRoutingList -join ',' ) ) )
                    {
                        $availabilityGroupReplica.ReadOnlyRoutingList.Clear()
                        foreach ( $readOnlyRoutingListEntry in $ReadOnlyRoutingList )
                        {
                            $availabilityGroupReplica.ReadOnlyRoutingList.Add($readOnlyRoutingListEntry) | Out-Null
                        }
                        $availabilityGroupReplicaUpdatesRequired = $true
                    }

                    if ( ( $submittedParameters -contains 'SeedingMode' ) -and ( $sqlMajorVersion -ge 13 ) -and ( $SeedingMode -ne $availabilityGroupReplica.SeedingMode ) )
                    {
                        if ((Get-Command -Name 'New-SqlAvailabilityReplica').Parameters.ContainsKey('SeedingMode'))
                        {
                            $availabilityGroupReplica.SeedingMode = $SeedingMode
                            $availabilityGroupReplicaUpdatesRequired = $true
                        }
                    }

                    if ( $availabilityGroupReplicaUpdatesRequired )
                    {
                        Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica
                    }
                }
                else
                {
                    $errorMessage = $script:localizedData.ReplicaNotFound -f $Name, $AvailabilityGroupName, $InstanceName
                    New-ObjectNotFoundException -Message $errorMessage
                }
            }
            else
            {
                # Connect to the instance that is supposed to house the primary replica
                $primaryReplicaServerObject = Connect-SQL -ServerName $PrimaryReplicaServerName -InstanceName $PrimaryReplicaInstanceName -ErrorAction 'Stop'

                # Verify the Availability Group exists on the supplied primary replica
                $primaryReplicaAvailabilityGroup = $primaryReplicaServerObject.AvailabilityGroups[$AvailabilityGroupName]
                if ( $primaryReplicaAvailabilityGroup )
                {
                    # Make sure the instance defined as the primary replica in the parameters is actually the primary replica
                    $primaryReplicaServerObject = Get-PrimaryReplicaServerObject -ServerObject $primaryReplicaServerObject -AvailabilityGroup $primaryReplicaAvailabilityGroup
                    $availabilityGroup = $primaryReplicaServerObject.AvailabilityGroups[$AvailabilityGroupName]

                    # Build the endpoint URL
                    $endpointUrl = "TCP://$($EndpointHostName):$($endpointPort)"

                    $newAvailabilityGroupReplicaParams = @{
                        Name             = $Name
                        InputObject      = $primaryReplicaAvailabilityGroup
                        AvailabilityMode = $AvailabilityMode
                        EndpointUrl      = $endpointUrl
                        FailoverMode     = $FailoverMode
                        Verbose          = $false
                    }

                    if ( $BackupPriority )
                    {
                        $newAvailabilityGroupReplicaParams.Add('BackupPriority', $BackupPriority)
                    }

                    if ( $ConnectionModeInPrimaryRole )
                    {
                        $newAvailabilityGroupReplicaParams.Add('ConnectionModeInPrimaryRole', $ConnectionModeInPrimaryRole)
                    }

                    if ( $ConnectionModeInSecondaryRole )
                    {
                        $newAvailabilityGroupReplicaParams.Add('ConnectionModeInSecondaryRole', $ConnectionModeInSecondaryRole)
                    }

                    if ( $ReadOnlyRoutingConnectionUrl )
                    {
                        $newAvailabilityGroupReplicaParams.Add('ReadOnlyRoutingConnectionUrl', $ReadOnlyRoutingConnectionUrl)
                    }

                    if ( $ReadOnlyRoutingList )
                    {
                        $newAvailabilityGroupReplicaParams.Add('ReadOnlyRoutingList', $ReadOnlyRoutingList)
                    }

                    if ( ( $sqlMajorVersion -ge 13 ) -and (Get-Command -Name 'New-SqlAvailabilityReplica').Parameters.ContainsKey('SeedingMode') )
                    {
                        $newAvailabilityGroupReplicaParams.Add('SeedingMode', $SeedingMode)
                    }

                    # Create the Availability Group Replica
                    try
                    {
                        Write-Verbose -Message (
                            $script:localizedData.PrepareAvailabilityReplica -f $Name, $AvailabilityGroupName, $InstanceName
                        )

                        $availabilityGroupReplica = New-SqlAvailabilityReplica @newAvailabilityGroupReplicaParams
                    }
                    catch
                    {
                        $errorMessage = $script:localizedData.FailedCreateAvailabilityGroupReplica -f $Name, $AvailabilityGroupName, $InstanceName
                        New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
                    }

                    # Join the Availability Group Replica to the Availability Group
                    try
                    {
                        Write-Verbose -Message (
                            $script:localizedData.JoinAvailabilityGroup -f $Name, $AvailabilityGroupName, $InstanceName
                        )

                        Join-SqlAvailabilityGroup -Name $AvailabilityGroupName -InputObject $serverObject | Out-Null
                    }
                    catch
                    {
                        $errorMessage = $script:localizedData.FailedJoinAvailabilityGroup -f $Name, $AvailabilityGroupName, $InstanceName
                        New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
                    }
                }
                # The Availability Group doesn't exist on the primary replica
                else
                {
                    $errorMessage = $script:localizedData.AvailabilityGroupNotFound -f $AvailabilityGroupName, $InstanceName
                    New-ObjectNotFoundException -Message $errorMessage
                }
            }
        }
    }
}

<#
    .SYNOPSIS
        Determines if the availability group replica is in the desired state.
 
    .PARAMETER Name
        The name of the availability group replica. For named instances this
        must be in the following format ServerName\InstanceName.
 
    .PARAMETER AvailabilityGroupName
        The name of the availability group.
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
 
    .PARAMETER PrimaryReplicaServerName
        Hostname of the SQL Server where the primary replica is expected to be active. If the primary replica is not found here, the resource will attempt to find the host that holds the primary replica and connect to it.
 
    .PARAMETER PrimaryReplicaInstanceName
        Name of the SQL instance where the primary replica lives.
 
    .PARAMETER Ensure
        Specifies if the availability group should be present or absent. Default is Present.
 
    .PARAMETER AvailabilityMode
        Specifies the replica availability mode. Default when creating a replica is 'AsynchronousCommit'.
 
    .PARAMETER BackupPriority
        Specifies the desired priority of the replicas in performing backups. The acceptable values for this parameter are integers from 0 through 100. Of the set of replicas which are online and available, the replica that has the highest priority performs the backup. When creating a replica the default is 50.
 
    .PARAMETER ConnectionModeInPrimaryRole
        Specifies how the availability replica handles connections when in the primary role.
 
    .PARAMETER ConnectionModeInSecondaryRole
        Specifies how the availability replica handles connections when in the secondary role.
 
    .PARAMETER EndpointHostName
        Specifies the hostname or IP address of the availability group replica endpoint. wWhen creating a group the default is the instance network name which is set in the code because the value can only be determined when connected to the SQL Instance.
 
    .PARAMETER FailoverMode
        Specifies the failover mode. When creating a replica the default is 'Manual'.
 
    .PARAMETER ReadOnlyRoutingConnectionUrl
        Specifies the fully-qualified domain name (FQDN) and port to use when routing to the replica for read only connections.
 
    .PARAMETER ReadOnlyRoutingList
        Specifies an ordered list of replica server names that represent the probe sequence for connection director to use when redirecting read-only connections through this availability replica. This parameter applies if the availability replica is the current primary replica of the availability group.
 
    .PARAMETER ProcessOnlyOnActiveNode
        Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.
 
    .PARAMETER SeedingMode
        Specifies the seeding mode. When creating a replica the default is 'Manual'.
        This parameter can only be used when the module SqlServer is installed.
#>

function Test-TargetResource
{
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('SqlServerDsc.AnalyzerRules\Measure-CommandsNeededToLoadSMO', '', Justification = 'The command Connect-Sql is called when Get-TargetResource is called')]
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

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

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

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

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

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

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present',

        [Parameter()]
        [ValidateSet('AsynchronousCommit', 'SynchronousCommit')]
        [System.String]
        $AvailabilityMode,

        [Parameter()]
        [ValidateRange(0, 100)]
        [System.UInt32]
        $BackupPriority,

        [Parameter()]
        [ValidateSet('AllowAllConnections', 'AllowReadWriteConnections')]
        [System.String]
        $ConnectionModeInPrimaryRole,

        [Parameter()]
        [ValidateSet('AllowNoConnections', 'AllowReadIntentConnectionsOnly', 'AllowAllConnections')]
        [System.String]
        $ConnectionModeInSecondaryRole,

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

        [Parameter()]
        [ValidateSet('Automatic', 'Manual')]
        [System.String]
        $FailoverMode,

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

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

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

        [Parameter()]
        [ValidateSet('Automatic', 'Manual')]
        [System.String]
        $SeedingMode = 'Manual'
    )

    $getTargetResourceParameters = @{
        InstanceName          = $InstanceName
        ServerName            = $ServerName
        Name                  = $Name
        AvailabilityGroupName = $AvailabilityGroupName
    }

    # Assume this will pass. We will determine otherwise later
    $result = $true

    $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters

    <#
        If this is supposed to process only the active node, and this is not the
        active node, don't bother evaluating the test.
    #>

    if ( $ProcessOnlyOnActiveNode -and -not $getTargetResourceResult.IsActiveNode )
    {
        Write-Verbose -Message (
            $script:localizedData.NotActiveNode -f (Get-ComputerName), $InstanceName
        )

        return $result
    }

    Write-Verbose -Message (
        $script:localizedData.TestingConfiguration -f $Name, $InstanceName
    )

    switch ($Ensure)
    {
        'Absent'
        {
            if ( $getTargetResourceResult.Ensure -eq 'Absent' )
            {
                $result = $true
            }
            else
            {
                $result = $false
            }
        }

        'Present'
        {
            $parametersToCheck = @(
                'AvailabilityMode',
                'BackupPriority',
                'ConnectionModeInPrimaryRole',
                'ConnectionModeInSecondaryRole',
                'FailoverMode',
                'ReadOnlyRoutingConnectionUrl',
                'ReadOnlyRoutingList'
            )
            if ( $getTargetResourceResult.SeedingMode)
            {
                $parametersToCheck += 'SeedingMode'
            }

            if ( $getTargetResourceResult.Ensure -eq 'Present' )
            {
                foreach ( $parameter in $PSBoundParameters.GetEnumerator() )
                {
                    $parameterName = $parameter.Key
                    $parameterValue = Get-Variable -Name $parameterName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Value

                    # Make sure we don't try to validate a common parameter
                    if ( $parametersToCheck -notcontains $parameterName )
                    {
                        continue
                    }

                    if ( $parameterName -eq 'ReadOnlyRoutingList' )
                    {
                        $different = ( $getTargetResourceResult.($parameterName) -join ',' ) -ne ( $parameterValue -join ',' )
                    }
                    else
                    {
                        $different = $getTargetResourceResult.($parameterName) -ne $parameterValue
                    }

                    if ( $different )
                    {
                        Write-Verbose -Message (
                            $script:localizedData.ParameterNotInDesiredState -f $parameterName, $parameterValue, $getTargetResourceResult.$parameterName
                        )

                        $result = $false
                    }
                }

                # Get the Endpoint URL properties
                $currentEndpointProtocol, $currentEndpointHostName, $currentEndpointPort = $getTargetResourceResult.EndpointUrl.Replace('//', '').Split(':')

                # Verify the hostname in the endpoint URL is correct
                if ( $PSBoundParameters.ContainsKey('EndpointHostName') -and $EndpointHostName -ne $currentEndpointHostName )
                {
                    Write-Verbose -Message (
                        $script:localizedData.ParameterNotInDesiredState -f 'EndpointHostName', $EndpointHostName, $currentEndpointHostName
                    )

                    $result = $false
                }

                # Verify the protocol in the endpoint URL is correct
                if ( 'TCP' -ne $currentEndpointProtocol )
                {
                    Write-Verbose -Message (
                        $script:localizedData.ParameterNotInDesiredState -f 'EndpointProtocol', 'TCP', $currentEndpointProtocol
                    )

                    $result = $false
                }

                # Verify the port in the endpoint URL is correct
                if ( $getTargetResourceResult.EndpointPort -ne $currentEndpointPort )
                {
                    Write-Verbose -Message (
                        $script:localizedData.ParameterNotInDesiredState -f 'EndpointPort', $getTargetResourceResult.EndpointPort, $currentEndpointPort
                    )

                    $result = $false
                }
            }
            else
            {
                $result = $false
            }
        }
    }

    return $result
}