DSCResources/DSC_SqlWaitForAG/DSC_SqlWaitForAG.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
        Returns the cluster role/group that is waiting to be created,
        along with the time and number of times to wait.
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
 
    .PARAMETER Name
        Name of the cluster role/group to look for (normally the same as the
        Availability Group name).
 
    .PARAMETER RetryIntervalSec
        The interval, in seconds, to check for the presence of the cluster role/group.
        Default value is 20 seconds. When the cluster role/group has been found the
        resource will check if the AG group exist. When the availability group has
        been found the resource will also wait this amount of time before returning.
 
    .PARAMETER RetryCount
        Maximum number of retries until the resource will timeout and throw an error.
        Default values is 30 times.
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $ServerName = (Get-ComputerName),

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

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

        [Parameter()]
        [System.UInt64]
        $RetryIntervalSec = 20,

        [Parameter()]
        [System.UInt32]
        $RetryCount = 30
    )

    Write-Verbose -Message (
        $script:localizedData.GetCurrentState -f $Name
    )

    $clusterGroupFound = $false

    # No ClusterName specified, so defaults to cluster on this node.
    $clusterGroup = Get-ClusterGroup -Name $Name -ErrorAction SilentlyContinue

    if ($null -ne $clusterGroup)
    {
        Write-Verbose -Message (
            $script:localizedData.FoundClusterGroup -f $Name
        )

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

        if ($serverObject)
        {
            # Determine if HADR is enabled on the instance. If not, AG group can not exist.
            if ($serverObject.IsHadrEnabled )
            {
                $availabilityGroup = $serverObject.AvailabilityGroups[$Name]

                if ( $availabilityGroup )
                {
                    $clusterGroupFound = $true
                }
                else
                {
                    Write-Verbose -Message (
                        $script:localizedData.AGNotFound -f $name, $InstanceName, $RetryIntervalSec
                    )
                }
            }
            else
            {
                Write-Verbose -Message (
                    $script:localizedData.HadrNotEnabled -f $InstanceName
                )
            }
        }
    }
    else
    {
        Write-Verbose -Message (
            $script:localizedData.MissingClusterGroup -f $Name
        )
    }

    return @{
        ServerName       = $ServerName
        InstanceName     = $InstanceName
        Name             = $Name
        RetryIntervalSec = $RetryIntervalSec
        RetryCount       = $RetryCount
        GroupExist       = $clusterGroupFound
    }
}

<#
    .SYNOPSIS
        Waits for a cluster role/group to be created
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
 
    .PARAMETER Name
        Name of the cluster role/group to look for (normally the same as the
        Availability Group name).
 
    .PARAMETER RetryIntervalSec
        The interval, in seconds, to check for the presence of the cluster role/group.
        Default value is 20 seconds. When the cluster role/group has been found the
        resource will check if the AG group exist. When the availability group has
        been found the resource will also wait this amount of time before returning.
 
 
    .PARAMETER RetryCount
        Maximum number of retries until the resource will timeout and throw an error.
        Default values is 30 times.
#>

function Set-TargetResource
{
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('SqlServerDsc.AnalyzerRules\Measure-CommandsNeededToLoadSMO', '', Justification='The command Connect-Sql is called when Get-TargetResource is called')]
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $ServerName = (Get-ComputerName),

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

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

        [Parameter()]
        [System.UInt64]
        $RetryIntervalSec = 20,

        [Parameter()]
        [System.UInt32]
        $RetryCount = 30
    )

    Write-Verbose -Message (
        $script:localizedData.WaitingClusterGroup -f $Name, $RetryCount, ($RetryIntervalSec * $RetryCount)
    )

    $getTargetResourceParameters = @{
        ServerName       = $ServerName
        InstanceName     = $InstanceName
        Name             = $Name
        RetryIntervalSec = $RetryIntervalSec
        RetryCount       = $RetryCount
    }

    for ($forLoopCount = 0; $forLoopCount -lt $RetryCount; $forLoopCount++)
    {
        $clusterGroupFound = (Get-TargetResource @getTargetResourceParameters).GroupExist
        if ($clusterGroupFound)
        {
            Write-Verbose -Message (
                '{0} {1}' -f `
                    ($script:localizedData.FoundClusterGroup -f $Name, $RetryCount, ($RetryIntervalSec * $RetryCount)),
                    ($script:localizedData.SleepMessage -f $RetryIntervalSec)
            )

            Start-Sleep -Seconds $RetryIntervalSec
            break
        }

        Write-Verbose -Message (
            '{0} {1}' -f `
                ($script:localizedData.MissingClusterGroup -f $Name, $RetryCount, ($RetryIntervalSec * $RetryCount)),
                ($script:localizedData.RetryMessage -f $RetryIntervalSec)
        )

        Start-Sleep -Seconds $RetryIntervalSec
    }

    if (-not $clusterGroupFound)
    {
        $errorMessage = $script:localizedData.FailedMessage -f $Name
        New-InvalidOperationException -Message $errorMessage
    }
}

<#
    .SYNOPSIS
        Tests if the cluster role/group has been created.
 
    .PARAMETER ServerName
        Hostname of the SQL Server to be configured.
 
    .PARAMETER InstanceName
        Name of the SQL instance to be configured.
 
    .PARAMETER Name
        Name of the cluster role/group to look for (normally the same as the
        Availability Group name).
 
    .PARAMETER RetryIntervalSec
        The interval, in seconds, to check for the presence of the cluster role/group.
        Default value is 20 seconds. When the cluster role/group has been found the
        resource will check if the AG group exist. When the availability group has
        been found the resource will also wait this amount of time before returning.
 
 
    .PARAMETER RetryCount
        Maximum number of retries until the resource will timeout and throw an error.
        Default values is 30 times.
#>

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()]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $ServerName = (Get-ComputerName),

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

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

        [Parameter()]
        [System.UInt64]
        $RetryIntervalSec = 20,

        [Parameter()]
        [System.UInt32]
        $RetryCount = 30
    )

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

    $getTargetResourceParameters = @{
        ServerName       = $ServerName
        InstanceName     = $InstanceName
        Name             = $Name
        RetryIntervalSec = $RetryIntervalSec
        RetryCount       = $RetryCount
    }

    $clusterGroupFound = (Get-TargetResource @getTargetResourceParameters).GroupExist
    if ($clusterGroupFound)
    {
        Write-Verbose -Message (
            $script:localizedData.FoundClusterGroup -f $Name
        )
    }
    else
    {
        Write-Verbose -Message (
            $script:localizedData.MissingClusterGroup -f $Name
        )
    }

    return $clusterGroupFound
}