SQL Always Availablity Groups

Hi,

I am setting DSC up for the first time and am attempting to setup a 3 node SQL server cluster with a number of AAGs spread across the 3 nodes. Each of the AAGs primary nodes are assigned to per-determined nodes.

I am having trouble generating the MOF file to including the AAG that corospsonds to the primary replica and the secondary replica that corosponds to the correct AAG. I have attached my configuration below. Would much apprciated if anyone could let me know if I am heading in the right direction or have gone about this the completly wrong way.

Thanks

@{
AllNodes = @(

@{

        NodeName = '*'

        Services = @(

            @{

                Name = 'FailoverClustering'

                ServiceName = 'Failover-clustering'

                Ensure = 'Present'

            },

            @{

                Name = 'RSATClusteringPS'

                ServiceName = 'RSAT-Clustering-PowerShell'

                Ensure = 'Present'

                DependsOn = '[WindowsFeature]FailoverClustering'

            },

            @{

                Name = 'RSATClusteringCMD'

                ServiceName = 'RSAT-Clustering-CmdInterface'

                Ensure = 'Present'

                DependsOn = '[WindowsFeature]RSATClusteringPS'

            },

            @{

                Name = 'RSATClusteringMgt'

                ServiceName ='RSAT-Clustering-Mgmt'

                Ensure = 'Present'

                DependsOn = '[WindowsFeature]RSATClusteringCMD'

            },

            @{

                Name = 'RSATADTools'

                ServiceName = 'RSAT-AD-Tools'

                Ensure = 'Present'

            }

        )

    },

@{

        NodeName = 'db81.domain.local.au'

        NodeType = 'Primary'

        CertificateFile = '\\ms01\DSC Pull Server$\Client Certificates\DB81.cer'

        IpAddress = 'x.x.x.x'

        DataDriveID = '6000C29446A5EA71DBFBF4B977272861'

        LogDriveID = '6000C29D63F77CB77ACF254395532E17'

        BackupDriveID = '6000C2995C61DE00F7C99419C8295A4D'

    },

@{

        NodeName = 'db82.domain.local.au'

        NodeType = 'Additional'

        CertificateFile = '\\ms01\DSC Pull Server$\Client Certificates\DB82.cer'

        IpAddress = 'x.x.x.x'

        DataDriveID = '6000C29FB1F08C063F13546A62348670'

        LogDriveID = '6000C2971AA5F2B27F271C29CCF26C14'

        BackupDriveID = '6000C29A913211AD53E31FDCE9385A99'

    },

@{

        NodeName = 'db83.domain.local.au'

        NodeType = 'Additional'

        CertificateFile = '\\ms01\DSC Pull Server$\Client Certificates\DB83.cer'

        IpAddress = 'x.x.x.x'

        DataDriveID = '6000C294386D6E76A937DDB4BFA8A25F'

        LogDriveID = '6000C29C8CABEDFBD85C95FEC79078DF'

        BackupDriveID = '6000C29C3DBBD3D7843B8D5015C49BF4'

    }

)

FailoverCluster = @{

        ClusterName = 'CL02'

        StaticIPAddress ='x.x.x.x'

}

DiskDrive = @{

        DataDrive = 'G'

        DataFSLabel = 'Data Drive'

        LogDrive = 'L'

        LogFSLabel = 'Log Drive'

        BackupDrive = 'R'

        BackupFSLabel = 'Backup Drive'

}

SQLConf = @{

        InstanceName = 'CL02'

        Features = 'SQLENGINE'

        SQLCollation = "SQL_Latin1_General_CP1_CI_AS"

        SQLSvcAccount = "domain\svcSQL_DBEngine$"

        AgtSvcAccount = "domain\svcSQL_Agent$"

        SQLSysAdminAccounts = 'domain\RBA_domain_SQL-ADMINISTRATORS'

        InstallSharedDir = "$env:ProgramFiles\Microsoft SQL Server\"

        InstallSharedWOWDir = "${env:CommonProgramFiles(x86)}\Microsoft SQL Server\"

        InstanceDir = "$env:ProgramFiles\Microsoft SQL Server\"

        InstallSQLDataDir = 'G:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data'

        SQLUserDBDir = 'G:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data'

        SQLUserDBLogDir = 'L:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data'

        SQLTempDBDir = 'G:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data'

        SQLTempDBLogDir = 'L:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data'

        SQLBackupDir = 'R:\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup'

        HADREndpointName = 'AAGEndpoint'

        HADREndpointPort = 5022

        SQLPort = 1434

}



SQLAAG =@(

    @{

        AvailabilityGroupName = 'CL02-AG1'

        AvailabilityGroupListenIP = 'x.x.x.x'

        AvailabilityGroupListenName = 'CL02-AG1L'

        AGPort = 1433

        PrimaryReplica = 'db81.domain.local.au'

        SecondayReplica = @('B82.domain.local.au', 'db83.domain.local.au')

    },

    @{

        AvailabilityGroupName = 'CL02-AG2'

        AvailabilityGroupListenIP = 'x.x.x.x'

        AvailabilityGroupListenName = 'CL02-AG2L'

        AGPort = 1433

        PrimaryReplica = 'db82.domain.local.au'

        SecondayReplica = @('db81.domain.local.au', 'db83.domain.local.au')

    },

    @{

        AvailabilityGroupName = 'CL02-AG3'

        AvailabilityGroupListenIP = 'x.x.x.x'

        AvailabilityGroupListenName = 'CL02-AG3L'

        AGPort = 1433

        PrimaryReplica = 'db83.domain.local.au'

        SecondayReplica = @('db81.domain.local.au', 'db82.domain.local.au')

    }

)



# Firewall Rules Configuration

FirewallRules = @(

    @{

        Ensure = 'Present'

        Name = "SQL Server Replication (TCP-In)"

        DisplayName = "SQL Server Replication (TCP-In)"

        Description = 'Allows inbound Microsoft SQL connections'

        Group = 'SQLServer'

        Enabled = 'True'

        Action = 'Allow'

        Direction = 'Inbound'

        LocalPort = 5022

        Protocol = 'TCP'

    },

    @{

        Ensure = 'Present'

        Name = "SQL Server Data (TCP-In)"

        DisplayName = "SQL Server Data (TCP-In)"

        Description = 'Allows inbound Microsoft SQL connections'

        Group = 'SQLServer'

        Enabled = 'True'

        Action = 'Allow'

        Direction = 'Inbound'

        LocalPort = 1433

        Protocol = 'TCP' 

    },

    @{

        Ensure = 'Present'

        Name = "SQL Server Browser (UDP-In)"

        DisplayName = "SQL Server Browser (UDP-In)"

        Description = 'Allows inbound Microsft SQL browser connections'

        Group = 'SQLServer'

        Enabled = 'True'

        Action = 'Allow'

        Direction = 'Inbound'

        LocalPort = 1433

        Protocol = 'TCP' 

    }

)

}

Configuration SQLInstall {
param (

    [Parameter(Mandatory = $true)]

    [pscredential] $ADUserCreds,

    [Parameter(Mandatory)]

    [pscredential]$svcSQLDBCreds,

    [Parameter(Mandatory)]

    [pscredential]$svcSQLAgentCreds

)

# Call Client ConfiurationData Environmentals

$DiskDrive = $ConfigurationData.DiskDrive

$SQLConf = $ConfigurationData.SQLConf

$SQLAAG = $ConfigurationData.SQLAAG

# Import DSC Modules

Import-DscResource -ModuleName 'PSDesiredStateConfiguration'

Import-DscResource -ModuleName 'xPSDesiredStateConfiguration'

Import-DscResource -ModuleName xfailovercluster

Import-DscResource -ModuleName StorageDsc

Import-DscResource -ModuleName sqlserverdsc

Import-DscResource -ModuleName networkingdsc

Import-DscResource -ModuleName activedirectorydsc

Node $AllNodes.NodeName

{

    # Enable required features

    foreach ($Service in $Node.Services){

        WindowsFeature $Service.Name

        {

            Ensure = $Service.Ensure

            Name = $Service.ServiceName

            DependsOn = $Service.DependsOn

        }  

    }

    # Check for primary node for cluster creation

    if ($Node.NodeType -eq 'Primary'){

        # Create new Cluster

        xCluster AddCluster {

            Name = $ConfigurationData.FailoverCluster.ClusterName

            StaticIPAddress = $ConfigurationData.FailoverCluster.StaticIPAddress

            DomainAdministratorCredential = $ADUserCreds

            PsDscRunAsCredential = $ADUserCreds

            DependsOn = '[WindowsFeature]RSATClusteringCMD'

        }

        # Set Cluster Quorum Settings

        xClusterQuorum SetQuorum{

            IsSingleInstance = 'Yes'

            Type = 'NodeMajority'

            DependsOn = '[xCluster]AddCluster'

        }

        # Add create computer objects permission to Cluster Name Object (CNO)

        ADObjectPermissionEntry AddCreateComputerAccountPermissionCNO{

            Ensure = 'Present'

            Path = 'OU=Servers,OU=Domain,DC=Domain,DC=local,DC=au'

            IdentityReference = "Domain\" + $ConfigurationData.FailoverCluster.ClusterName + '$'

            AccessControlType = 'Allow'

            ActiveDirectoryRights = 'CreateChild', 'DeleteChild'

            ObjectType = 'bf967a86-0de6-11d0-a285-00aa003049e2' # All computer objects

            ActiveDirectorySecurityInheritance = 'All'

            InheritedObjectType = '00000000-0000-0000-0000-000000000000'

            DependsOn = '[xClusterQuorum]SetQuorum'

            PsDscRunAsCredential = $ADUserCreds

        }

        $DependsOnVariable = "[xCluster]AddCluster"

    } else { # Secondary Node

        # Wait for Cluster creation

            xWaitForCluster WaitForCluster {

                Name = $ConfigurationData.FailoverCluster.ClusterName

                RetryIntervalSec = 10

                RetryCount = 60

                DependsOn = '[WindowsFeature]RSATClusteringCMD'

            }

            # Create new Cluster

            xCluster JoinCluster {

                Name = $ConfigurationData.FailoverCluster.ClusterName

                StaticIPAddress = $ConfigurationData.FailoverCluster.StaticIPAddress

                DomainAdministratorCredential = $ADUserCreds

                PsDscRunAsCredential = $ADUserCreds

                DependsOn = '[xWaitForCluster]WaitForCluster'

            }

            $DependsOnVariable = "[xWaitForCluster]WaitForCluster"

    }# End Else  

            

    # Set initialize disk and create volumes

    WaitForDisk DataDrive {

        DiskId = $Node.DataDriveID

        DiskIdType = 'UniqueId'

        RetryIntervalSec = 60

        RetryCount = 60

        DependsOn = $DependsOnVariable

    }

    Disk DataDrive {

        DiskId = $Node.DataDriveID

        DiskIdType = 'UniqueId'

        DriveLetter = $DiskDrive.DataDrive

        FSLabel = $DiskDrive.DataFSLabel

        DependsOn = '[WaitForDisk]DataDrive'

    }

    WaitForDisk LogDrive {

        DiskId = $Node.LogDriveID

        DiskIdType = 'UniqueId'

        RetryIntervalSec = 60

        RetryCount = 60

        DependsOn = $DependsOnVariable

    }

    Disk LogDrive {

        DiskId = $Node.LogDriveID

        DiskIdType = 'UniqueId'

        DriveLetter = $DiskDrive.LogDrive

        FSLabel = $DiskDrive.LogFSLabel

        DependsOn = '[WaitForDisk]LogDrive'

    } 

    

    WaitForDisk BackupDrive {

        DiskId = $Node.BackupDriveID

        DiskIdType = 'UniqueId'

        RetryIntervalSec = 60

        RetryCount = 60

        DependsOn = $DependsOnVariable

    }

    Disk BackupDrive {

        DiskId = $Node.BackupDriveID

        DiskIdType = 'UniqueId'

        DriveLetter = $DiskDrive.BackupDrive

        FSLabel = $DiskDrive.BackupFSLabel

        DependsOn = '[WaitForDisk]BackupDrive'

    }

    # Create SQL Directories

    File CreateDataDir {

        Ensure = 'Present'

        DestinationPath = $SQLConf.InstallSQLDataDir

        Type = 'Directory'

        DependsOn = '[Disk]DataDrive'

    }

    File CreateLogDir {

        Ensure = 'Present'

        DestinationPath = $SQLConf.SQLUserDBLogDir

        Type = 'Directory'

        DependsOn = '[Disk]LogDrive'

    }

    File CreateBackupDir {

        Ensure = 'Present'

        DestinationPath = $SQLConf.SQLBackupDir

        Type = 'Directory'

        DependsOn = '[Disk]BackupDrive'

    }

    # Check and set SQL Managed Service Account

    Script SetManagedServiceAccountAgent {

        SetScript = {

            Install-ADServiceAccount -Identity svcSQL_Agent

        }

        TestScript = {

            Test-ADServiceAccount -Identity svcSQL_Agent

        }

        GetScript = {

            Test-ADServiceAccount -Identity svcSQL_Agent

        }

    }

    Script SetManagedServiceAccountDBEngine {

        SetScript = {

            Install-ADServiceAccount -Identity svcSQL_DBEngine

        }

        TestScript = {

            Test-ADServiceAccount -Identity svcSQL_DBEngine

        }

        GetScript = {

            Test-ADServiceAccount -Identity svcSQL_DBEngine

        }

    }

    #Install SQL Server

    SqlSetup BaseInstall {

        InstanceName = $SQLConf.InstanceName

        Action = 'Install'

        SqlSvcStartupType = 'Automatic'

        SQLSvcAccount = $svcSQLDBCreds

        AgtSvcAccount = $svcSQLAgentCreds

        SQLSysAdminAccounts = $SQLConf.SQLSysAdminAccounts

        Features = $SQLConf.Features

        InstallSharedDir = $SQLConf.InstallSharedDir

        InstallSharedWOWDir = $SQLConf.InstallSharedWOWDir

        InstanceDir = $SQLConf.InstanceDir

        InstallSQLDataDir = $SQLConf.InstallSQLDataDir

        SQLUserDBDir = $SQLConf.SQLUserDBDir

        SQLUserDBLogDir = $SQLConf.SQLUserDBLogDir

        SQLBackupDir = $SQLConf.SQLBackupDir

        SQLTempDBDir = $SQLConf.SQLTempDBDir

        SQLTempDBLogDir = $SQLConf.SQLTempDBLogDir

        DependsOn = '[Script]SetManagedServiceAccountAgent', '[Script]SetManagedServiceAccountDBEngine', '[Disk]BackupDrive', '[Disk]LogDrive', '[Disk]DataDrive', $DependsOnVariable

        PsDscRunAsCredential = $ADUserCreds

        SourcePath = '\\ms01\admins\Software Replicated\Installation Software\Microsoft\SQL\SQL Server 2017\Installs'

        SuppressReboot = $true

        UpdateEnabled = $true

        UpdateSource = '\\ms01\admins\Software Replicated\Installation Software\Microsoft\SQL\SQL Server 2017\Updates'

    }

    SqlServerNetwork SetTCP {

        InstanceName = $SQLConf.InstanceName

        ProtocolName = 'TCP'

        IsEnabled = $true

        TcpDynamicPort = $false

        TcpPort = $SQLConf.SQLPort

        RestartService = $true

        DependsOn = '[SqlSetup]BaseInstall'

    }

    SqlServerLogin AddNTServiceClusSvc {

        Ensure = 'Present'

        Name = 'NT SERVICE\ClusSvc'

        ServerName = $Node.NodeName

        InstanceName = $SQLConf.InstanceName

        DependsOn = '[SqlSetup]BaseInstall'

    }

    SqlServerPermission AddNTServiceClusSvcPermission {

        Ensure = 'Present'

        ServerName = $Node.NodeName

        InstanceName = $SQLConf.InstanceName

        Principal = 'NT SERVICE\ClusSvc'

        Permission = 'AlterAnyAvailabilityGroup', 'ViewServerState'

        DependsOn = '[SqlServerLogin]AddNTServiceClusSvc'

    }

    SqlAlwaysOnService EnableAAG {

        ServerName = $Node.NodeName

        InstanceName = $SQLConf.InstanceName

        Ensure = 'Present'

        DependsOn = '[SqlSetup]BaseInstall'

    }

    SqlServerEndpoint $SQLConf.HADREndpointName {

        EndpointName = $SQLConf.HADREndpointName

        Port = $SQLConf.HADREndpointPort

        Ensure = 'Present'

        ServerName = $Node.NodeName

        InstanceName = $SQLConf.InstanceName

        DependsOn = '[SqlAlwaysOnService]EnableAAG'

    }

    SqlServerEndpointState $SQLConf.HADREndpointName {

        Name = $SQLConf.HADREndpointName

        InstanceName = $SQLConf.InstanceName

        ServerName = $Node.NodeName

        State = 'Started'

        DependsOn = "[SqlServerEndpoint]$($SQLConf.HADREndpointName)"

    } 

    # Create Always on Availabilty Groups

    ForEach ($AAG in $SQLAAG){

        if ($AAG.PrimaryReplica -eq $Node.NodeName){

            SqlAG $AAG.AvailabilityGroupName {

                Name = $AAG.AvailabilityGroupName

                Ensure = 'Present'

                InstanceName = $SQLConf.InstanceName

                ServerName = $Node.NodeName

                FailoverMode = 'Automatic'

                AvailabilityMode = 'SynchronousCommit'

                # ConnectionModeInPrimaryRole = 'AllowAllConnections'

                # ConnectionModeInSecondaryRole = 'AllowNoConnections'

                # BasicAvailabilityGroup = $false

                # DtcSupportEnabled = $true

                PsDscRunAsCredential = $ADUserCreds

                DependsOn = "[SqlServerEndpointState]$($SQLConf.HADREndpointName)", '[SqlServerPermission]AddNTServiceClusSvcPermission'

            }

            SqlAGListener $AAG.AvailabilityGroupListenName {

                InstanceName = $SQLConf.InstanceName

                AvailabilityGroup = $AAG.AvailabilityGroupName

                ServerName = $Node.NodeName

                Name = $AAG.AvailabilityGroupListenName

                IpAddress = $AAG.AvailabilityGroupListenIP

                Port = $AAG.AGPort

                Ensure = 'Present'

                DependsOn = "[SqlAG]$($AAG.AvailabilityGroupName)"

                PsDscRunAsCredential = $ADUserCreds

            }

        }

        if ($AAG.SecondayReplica -eq $Node.NodeName){

            SqlWaitForAG "WaitFor$($AAG.AvailabilityGroupName)" {

                Name = $AAG.AvailabilityGroupName

                RetryIntervalSec = 20

                RetryCount = 70

                DependsOn = "[SqlServerEndpointState]$($SQLConf.HADREndpointName)"

            }

            SqlAGReplica $AAG.AvailabilityGroupName {

                Ensure = 'Present'

                Name = $AAG.PrimaryReplica

                AvailabilityGroupName = $AAG.AvailabilityGroupName

                ServerName = $Node.Where{$_.NodeType -eq 'Primary'}.NodeName

                InstanceName = $SQLConf.InstanceName

                PrimaryReplicaInstanceName = $SQLConf.InstanceName

                PrimaryReplicaServerName = $AAG.PrimaryReplica

                FailoverMode = 'Automatic'

                AvailabilityMode = 'SynchronousCommit'

                DependsOn = "[SqlWaitForAG]WaitFor$($AAG.AvailabilityGroupName)" 

                PsDscRunAsCredential = $ADUserCreds

            } 

        }

    }

    # Add firewall rules

    foreach ($Rule in $Node.FirewallRulles){

        Firewall SQLServerBrowser {

            Ensure = $Rule.Ensure

            Name = $Rule.Name

            DisplayName = $Rule.DisplayName

            Description = $Rule.Description

            Group = $Rule.Group

            Enabled = $Rule.Enabled

            Action = $Rule.Action

            Direction = $Rule.Direction

            LocalPort = $Rule.LocalPort

            Protocol = $Rule.Protocol

        }

    }

}# End Primary Node 

}