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