Function Set-ConfigCheckpoint([string]$Checkpoint) { $CheckpointPath = "C:\FCIResources\Checkpoints\$${Checkpoint}" if (-not(Test-Path -Path $CheckpointPath -PathType Leaf)) { Write-Host "[Checkpoint] $Checkpoint" Set-Content -Value "$(Get-Date -format 'u')" -Path $CheckpointPath Restart-Computer -Force Exit } } New-Item -Path C:\\FCIResources -Name Checkpoints -ItemType Directory -ErrorAction SilentlyContinue "Get Credentials" $AdminUser = (Get-SECSecretValue -SecretId ${sqlcluster_user.username_secret}).SecretString $AdminPassword = (Get-SECSecretValue -SecretId ${sqlcluster_user.password_secret}).SecretString $SecureAdminPassword = ConvertTo-SecureString -String $AdminPassword -AsPlainText -Force $ClusterAdminUser = "${domain_net_bios_name}\$${AdminUser}" $ClusterCredentials = (New-Object PSCredential($ClusterAdminUser,$SecureAdminPassword)) $DomainUser = (Get-SECSecretValue -SecretId ${sqlservice_user.username_secret}).SecretString $DomainPassword = (Get-SECSecretValue -SecretId ${sqlservice_user.password_secret}).SecretString $FQDomainUser = "${domain_net_bios_name}\$${DomainUser}" "Config LCM and Cred Encrypt Cert" $CertificatePath = "C:\FCIResources\publickeys\AWSQSDscPublicKey.cer" if (-not(Test-Path -Path $CertificatePath)) { New-Item -Path C:\FCIResources\publickeys -ItemType directory $cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'AWSQSDscEncryptCert' -HashAlgorithm SHA256 $cert | Export-Certificate -FilePath $CertificatePath -Force $DscCertThumbprint = [string](get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint [DSCLocalConfigurationManager()] configuration LCMConfig { Node 'localhost' { Settings { RefreshMode = 'Push' ActionAfterReboot = 'ContinueConfiguration' RebootNodeIfNeeded = $false CertificateId = $DscCertThumbprint } } } LCMConfig -OutputPath 'C:\FCIResources\LCMConfig' } "Join AD" Add-Computer -DomainName ${domain_dns_name} -Credential $ClusterCredentials -Force -Restart "Configure Domain Groups" Add-LocalGroupMember -Group "Administrators" -Member ${domain_group_administrators} Add-LocalGroupMember -Group "Remote Desktop Users" -Member ${domain_group_rdp_users} Set-ConfigCheckpoint DomainJoined "Start LCM" Set-DscLocalConfigurationManager -Path 'C:\FCIResources\LCMConfig' $DscCertThumbprint = [string](get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint $LCMConfiguration = @{ AllNodes = @( @{ NodeName="*" CertificateFile = $CertificatePath Thumbprint = $DscCertThumbprint PSDscAllowDomainUser = $true }, @{ NodeName = 'localhost' } ) } "Set Static IPs" $networkInterface = (Get-NetAdapter | Where-Object { $_.InterfaceDescription -match '^Amazon Elastic Network Adapter'} | Sort-Object -Property ifIndex | Select-Object -First 1).ifIndex $dnsServerAddress = (Get-DnsClientServerAddress -InterfaceIndex $networkInterface -AddressFamily IPv4).ServerAddresses if ((Get-NetIPInterface -InterfaceIndex $networkInterface -AddressFamily IPv4).Dhcp -eq "Enabled") { Remove-NetIPAddress -InterfaceIndex $networkInterface -Confirm:$false New-NetIPAddress -InterfaceIndex $networkInterface -IPAddress ${network_primary_ip} -PrefixLength 24 -DefaultGateway ${network_default_gateway} -SkipAsSource:$false -Confirm:$false Set-DnsClientServerAddress -InterfaceIndex $networkInterface -ServerAddresses $dnsServerAddress -Confirm:$false Set-NetIPInterface -InterfaceIndex $networkInterface -Dhcp Disabled -Confirm:$false } $HostName = hostname Set-ConfigCheckpoint WindowsConfigured "Create FSx Share" $shareName = "${cluster_name}" Invoke-Command -ComputerName ${fsx_admin_endpoint} -ConfigurationName FSxRemoteAdmin -scriptblock { New-FSxSmbShare -Name $Using:shareName -Path "D:\share\" -Description "CA share for MSSQL FCI" %{ if cluster_size > 1 }-ContinuouslyAvailable $True%{ endif } -Credential $Using:ClusterCredentials } -Credential $ClusterCredentials Invoke-Command -ComputerName ${fsx_admin_endpoint} -ConfigurationName FSxRemoteAdmin -scriptblock { Grant-FSxSmbShareAccess -Name $Using:shareName -AccountName $Using:ClusterAdminUser,$Using:FQDomainUser,${domain_group_administrators} -AccessRight Full -force } -Credential $ClusterCredentials "Configure Witness SMB share on FSx" $WitnessShareName = "${cluster_name}-Witness" Invoke-Command -ComputerName ${fsx_admin_endpoint} -ConfigurationName FSxRemoteAdmin -scriptblock { New-FSxSmbShare -Name $Using:WitnessShareName -Path "D:\share\" -Description "Witness share for MSSQL FCI" %{ if cluster_size > 1 }-ContinuouslyAvailable $True%{ endif } -Credential $Using:ClusterCredentials } -Credential $ClusterCredentials Invoke-Command -ComputerName ${fsx_admin_endpoint} -ConfigurationName FSxRemoteAdmin -scriptblock { Grant-FSxSmbShareAccess -Name $Using:WitnessShareName -AccountName Everyone -AccessRight Change -force } -Credential $ClusterCredentials Configuration WSFCNode1Config { param( [PSCredential] $ClusterCredentials ) Import-DscResource -ModuleName FailoverClusterDsc Node 'localhost' { Cluster CreateCluster { Name = "FCI_${cluster_id}" StaticIPAddress = "${secondary_ips[0]}" DomainAdministratorCredential = $ClusterCredentials } WaitForCluster WaitForCluster { Name = "FCI_${cluster_id}" RetryIntervalSec = 30 RetryCount = 30 DependsOn = "[Cluster]CreateCluster" } ClusterQuorum FsxWitnessQuorum { IsSingleInstance = "Yes" Type = "NodeAndFileShareMajority" Resource = "\\${fsx_net_bios_name}\$${WitnessShareName}" DependsOn = "[WaitForCluster]WaitForCluster" } } } %{ if cluster_size > 1 ~} Configuration AdditionalWSFCNode { param( [PSCredential] $ClusterCredentials ) Import-DscResource -ModuleName FailoverClusterDsc Node 'localhost' { WaitForCluster WaitForCluster { Name = "FCI_${cluster_id}" RetryIntervalSec = 30 RetryCount = 30 } Cluster JoinNodeToCluster { Name = "FCI_${cluster_id}" StaticIPAddress = "${secondary_ips[0]}/24" DomainAdministratorCredential = $ClusterCredentials DependsOn = '[WaitForCluster]WaitForCluster' } ClusterIPAddress SecondaryIP { IPAddress = "${secondary_ips[2]}" Ensure = "Present" AddressMask = "255.255.255.0" DependsOn = "[Cluster]JoinNodeToCluster" } } } %{ endif ~} "Config Cluster Node ${cluster_node_index}" $NodeConfigFile = "C:\FCIResources\NodeConfig" %{ if primary_instance ~} WSFCNode1Config -OutputPath $NodeConfigFile -ConfigurationData $LCMConfiguration -ClusterCredentials $ClusterCredentials %{ else ~} AdditionalWSFCNode -OutputPath $NodeConfigFile -ConfigurationData $LCMConfiguration -ClusterCredentials $ClusterCredentials %{ endif ~} Start-DscConfiguration $NodeConfigFile -Wait -Verbose -Force Set-ConfigCheckpoint ClusterConfigured "Config AD ACL Rules, allows nodes to control the FCI CNO" Invoke-Command -scriptblock { $computer = get-adcomputer "FCI_${cluster_id}" $discard,$OU = $computer -split ',',2 $acl = get-acl "ad:$OU" $acl.access #to get access right of the OU $sid = [System.Security.Principal.SecurityIdentifier] $computer.SID $objectguid1 = new-object Guid bf967a86-0de6-11d0-a285-00aa003049e2 # is the rightsGuid for Create Computer Object class $inheritedobjectguid = new-object Guid bf967aa5-0de6-11d0-a285-00aa003049e2 # is the schemaIDGuid for the OU $identity = [System.Security.Principal.IdentityReference] $SID $adRights = [System.DirectoryServices.ActiveDirectoryRights] "CreateChild" $adRights2 = [System.DirectoryServices.ActiveDirectoryRights] "ReadProperty" $type = [System.Security.AccessControl.AccessControlType] "Allow" $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "All" $ace1 = new-object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$objectGuid1,$inheritanceType,$inheritedobjectguid $ACE2 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights2,$type,$inheritanceType $acl.AddAccessRule($ace1) $acl.AddAccessRule($ACE2) Set-acl -aclobject $acl "ad:$OU" } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp if (Test-Path "HKLM:\Software\Microsoft\Microsoft SQL Server\Instance Names\SQL") { "SQL already installed - exiting." Exit 0 } Else { "SQL not installed - continuing." } "Preparing SQL for Failover" $arguments = '/ACTION="PrepareFailoverCluster" /PID="${sqlserver_product_key}" /IAcceptSQLServerLicenseTerms="True" /IACCEPTROPENLICENSETERMS="False" /SUPPRESSPRIVACYSTATEMENTNOTICE="True" /ENU="True" /QUIET="True" /UpdateEnabled="False" /USEMICROSOFTUPDATE="False" /SUPPRESSPAIDEDITIONNOTICE="True" /UpdateSource="MU" /FEATURES=SQLENGINE,REPLICATION,FULLTEXT,DQ /HELP="False" /INDICATEPROGRESS="False" /X86="False" /INSTANCENAME="MSSQLSERVER" /INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" /INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" /INSTANCEID="MSSQLSERVER" /INSTANCEDIR="C:\Program Files\Microsoft SQL Server" /AGTSVCACCOUNT="{0}" /AGTSVCPASSWORD="{1}" /FILESTREAMLEVEL="0" /SQLSVCACCOUNT="{0}" /SQLSVCPASSWORD="{1}" /SQLSVCINSTANTFILEINIT="False" /FTSVCACCOUNT="NT Service\MSSQLFDLauncher" /SKIPRULES="ClusterPrepare_HasClusteredInstanceCheck"' -f $FQDomainUser, $DomainPassword Invoke-Command -scriptblock { Start-Process -FilePath C:\SQL_Install_Media\setup.exe -ArgumentList $Using:arguments -Wait -NoNewWindow } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp "Setup TempDB" Stop-Service -Name ShellHWDetection Get-Disk | Where PartitionStyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -DriveLetter "T" -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "TempDB" -Confirm:$false Start-Service -Name ShellHWDetection $sqlTempDbPath = "T:\SQLServer\tempdb" New-Item -Path $sqlTempDbPath -ItemType directory %{ if primary_instance ~} %{ if cluster_size > 1 ~} "Check all cluster networks are registered" do { "Wait 30 sec for networks to be available" Start-Sleep -Seconds 30 $getCN = Invoke-Command -scriptblock { (Get-ClusterNetwork -Cluster "FCI_${cluster_id}").length -lt ${cluster_size} } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp } while ($getCN) "Wait to complete failover prep" Start-Sleep -Seconds 300 %{ endif ~} "Complete SQL Cluster Installation" $sqlRootPath = "\\${fsx_net_bios_name}\${cluster_name}\${cluster_id}\mssql" $sqlDataPath = "\\${fsx_net_bios_name}\${cluster_name}\${cluster_id}\mssql\data" $sqlLogPath = "\\${fsx_net_bios_name}\${cluster_name}\${cluster_id}\mssql\logs" "Validate Cluster Status" Invoke-Command -scriptblock { Test-Cluster } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp "Complete SQL Setup" $arguments = '/QUIET /SECURITYMODE=SQL /SAPWD="${sql_sa_password}" /ACTION=CompleteFailoverCluster /InstanceName=MSSQLSERVER /INDICATEPROGRESS=FALSE /FAILOVERCLUSTERNETWORKNAME={0} /FAILOVERCLUSTERIPADDRESSES="IPv4;{5};Cluster Network 1;{6}" %{ if cluster_size > 1}"IPv4;{7};Cluster Network 2;{8}"%{ endif } /CONFIRMIPDEPENDENCYCHANGE=TRUE /FAILOVERCLUSTERGROUP="SQL Server (MSSQLSERVER)" /INSTALLSQLDATADIR="C:\Program Files\Microsoft SQL Server" /SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" /SQLSYSADMINACCOUNTS=${domain_group_administrators} /INSTALLSQLDATADIR={1} /SQLUSERDBDIR={2} /SQLUSERDBLOGDIR={3} /SQLTEMPDBDIR={4}' -f "${cluster_name}", $sqlRootPath, $sqlDataPath, $sqlLogPath, $sqlTempDbPath, "${secondary_ips[1]}", "255.255.255.0" %{ if cluster_size > 1 ~}, "${secondary_ips[3]}", "255.255.255.0" %{ endif } Invoke-Command -scriptblock { Start-Process -FilePath C:\SQL_Install_Media\setup.exe -ArgumentList $Using:arguments -Wait -NoNewWindow } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp %{ endif ~} "Check SQL Cluster Health" do { "Wait 5 sec registration" Start-Sleep -Seconds 5 $SqlGroup = Invoke-Command -scriptblock { (Get-ClusterGroup | Where-Object {$_.Name.StartsWith("SQL Server")}) } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp } while (!$SqlGroup) do { "Wait 5 sec for SQL IP Address Registration" Start-Sleep -Seconds 5 $SqlIpAddress = Invoke-Command -scriptblock { (Get-ClusterResource | Where-Object {$_.Name.StartsWith("SQL IP Address ${cluster_node_index}")}) } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp } while (!$SqlIpAddress) "FCI Health Check Probes" Invoke-Command -scriptblock { $SIP = Get-ClusterResource | Where-Object {$_.Name.StartsWith("SQL IP Address ${cluster_node_index}")} if (($SIP | get-clusterparameter -Name ProbePort | Format-Wide -Property Value | Out-String).Trim() -ne ${fci_health_check_port}) { "Set up Probes" $SIP | Set-ClusterParameter -Multiple @{ 'Address'="%{if cluster_node_index == 1}${secondary_ips[1]}%{ else }${secondary_ips[3]}%{ endif }"; 'ProbePort'=${fci_health_check_port}; 'SubnetMask'='255.255.255.0'; 'Network'=(Get-ClusterNetwork)[${cluster_node_index - 1}].Name; 'EnableDhcp'=0; } %{ if primary_instance ~} "Stop-Start Cluster Resource" $SIP | Stop-ClusterResource | Start-ClusterResource %{ endif ~} "Stop-Start Cluster Group" $SG = Get-ClusterGroup | Where-Object {$_.Name.StartsWith("SQL Server")} $SG | Stop-ClusterGroup | Start-ClusterGroup } } -Credential $ClusterCredentials -ComputerName $HostName -Authentication credssp "Installation Complete." true