215 lines
9.4 KiB
PowerShell
215 lines
9.4 KiB
PowerShell
function New-AlkamiServiceFabricCluster {
|
|
<#
|
|
.SYNOPSIS
|
|
Creates a service fabric cluster with the set of servers and a security group prefix.
|
|
Security group prefix is 'Stage' for Staging, 'DEV' for QA
|
|
.PARAMETER servers
|
|
The servers to join together into a service fabric cluster.
|
|
.PARAMETER securityGroupPrefix
|
|
The prefix of the "X - GMSA" security group that the servers are in.
|
|
.PARAMETER serverCertificateCommonName
|
|
The common name of the admin certificate used to administer the cluster, and secure the service fabric dashboard.
|
|
.PARAMETER clientCertificateCommonName
|
|
The common name of the client certificate used to gain read-only access to the cluster, and service fabric dashboard.
|
|
#>
|
|
[CmdletBinding()]
|
|
Param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$Servers,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SecurityGroupPrefix,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ServerCertificateCommonName,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ClientCertificateCommonName
|
|
)
|
|
|
|
$loglead = (Get-LogLeadName);
|
|
Write-Host "$loglead : Initializing Service Fabric cluster.";
|
|
|
|
# Determine if this cluster is being created for a non-production environment.
|
|
$devEnvironmentTypes = @(
|
|
"Development",
|
|
"QA",
|
|
"TeamQA",
|
|
"Sandbox"
|
|
)
|
|
$environmentType = (Get-AppSetting -appSettingKey "Environment.Type" -ComputerName $servers[0]);
|
|
$isDevEnvironment = $null -ne ($devEnvironmentTypes | Where-Object { $_ -eq $environmentType; });
|
|
|
|
$nodes = @();
|
|
foreach($server in $servers) {
|
|
# If the server name passed in is a FQDN, strip off everything after the "." for convenience of reading later.
|
|
$nodeName = $server;
|
|
$dotIndex = $nodeName.IndexOf(".");
|
|
if($dotIndex -ge 0) {
|
|
$nodeName = $nodeName.Substring(0, $dotIndex);
|
|
}
|
|
|
|
$hashcode = [Math]::Abs($server.GetHashCode());
|
|
$availabilityZone = Get-AvailabilityZone -ComputerName $server;
|
|
if($null -eq $availabilityZone) {
|
|
$availabilityZone = $hashcode;
|
|
}
|
|
$faultDomain = "fd:/{0}/r0" -f $availabilityZone;
|
|
|
|
$nodes += @{
|
|
NodeName = $nodeName;
|
|
IPAddress = $server;
|
|
FaultDomain = $faultDomain
|
|
UpgradeDomain = $hashcode;
|
|
}
|
|
}
|
|
|
|
# If it's a development environment and there are less than 3 AZ's, tack unique numbers onto the end of the AZ's.
|
|
# This is to trick Service Fabric into believing that it has 3 AZ's so that the cluster bootstraps successfully.
|
|
if($isDevEnvironment)
|
|
{
|
|
$uniqueFaultDomains = $nodes | Foreach-Object { $_["FaultDomain"] } | Select-Object -Unique;
|
|
if($uniqueFaultDomains.Count -lt 3)
|
|
{
|
|
$counter = 1;
|
|
foreach($node in $nodes)
|
|
{
|
|
$node["FaultDomain"] = $node["FaultDomain"] + "-$counter";
|
|
$counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$podName = (Get-AppSetting -appSettingKey "Environment.Name" -ComputerName $servers[0])
|
|
if($null -eq $podName) {
|
|
throw "$loglead : Environment.Name machine config app setting value must be specified!";
|
|
}
|
|
$podName = $podName.Replace(" ","-");
|
|
$clusterName = "$podName-Fabric";
|
|
Write-Host "$loglead : Creating Cluster $clusterName`n";
|
|
|
|
Write-Host "$loglead : Downloading Service Fabric installer/runtime files via Chocolatey."
|
|
choco upgrade Alkami.DevOps.ServiceFabric -y --no-progress;
|
|
|
|
$chocoInstallPath = Get-ChocolateyInstallPath
|
|
$fabricBasePath = Join-Path $chocoInstallPath "lib\Alkami.DevOps.ServiceFabric\files"
|
|
if(!(Test-Path $fabricBasePath)) {
|
|
throw "$loglead : Service Fabric was not downloaded correctly. Check the Chocolatey logs.";
|
|
}
|
|
|
|
# Make sure that the offline installation .cab was downloaded.
|
|
$runtime = Get-ChildItem -Path $fabricBasePath -Filter "*.cab" | Select-Object -First 1
|
|
if(($null -eq $runtime) -or (!(Test-Path $runtime.FullName))) {
|
|
throw "$loglead : The Service Fabric offline installation .cab was not downloaded correctly. Check the chocolatey logs."
|
|
}
|
|
$runtimePath = $runtime.FullName;
|
|
|
|
$templateFileName = "ClusterConfig.json";
|
|
$fabricConfigTemplatePath = (Join-Path $PSScriptRoot "ServiceFabricConfigTemplates/$templateFileName");
|
|
$clusterConfigTemplatePath = (Join-Path $fabricBasePath $templateFileName);
|
|
Copy-Item -Path $fabricConfigTemplatePath -Destination $clusterConfigTemplatePath -Force
|
|
|
|
if(!(Test-Path $clusterConfigTemplatePath)) {
|
|
throw "$loglead : Cannot locate cluster configuration template file.";
|
|
}
|
|
|
|
Write-Host "`n$loglead : Building service fabric cluster definition.";
|
|
$config = ((Get-Content -Path $clusterConfigTemplatePath) | ConvertFrom-Json);
|
|
|
|
# Define cluster name.
|
|
$config.name = $clusterName;
|
|
|
|
# Define nodes in the cluster.
|
|
$tempNode = $config.nodes[0].PsObject.Copy();
|
|
$config.nodes = @();
|
|
foreach($serverNode in $nodes) {
|
|
$xmlNode = $tempNode.PsObject.Copy();
|
|
|
|
$xmlNode.nodeName = $serverNode.NodeName;
|
|
$xmlNode.ipAddress = $serverNode.IPAddress;
|
|
$xmlNode.faultDomain = $serverNode.FaultDomain;
|
|
$xmlNode.upgradeDomain = $serverNode.UpgradeDomain;
|
|
|
|
$config.nodes += $xmlNode;
|
|
}
|
|
|
|
# Configure gmsa cluster security.
|
|
Write-Host "$loglead : Now configuring GMSA account security for the cluster.";
|
|
$gmsaGroup = "$securityGroupPrefix - gMSA";
|
|
|
|
Write-Host "$loglead : Configuring security within the `"$gmsaGroup`" group.";
|
|
$config.properties.security.WindowsIdentities.ClusterIdentity = $gmsaGroup;
|
|
|
|
Write-Host "$loglead : Configuring certificates."
|
|
$config.properties.security.CertificateInformation.ServerCertificateCommonNames.CommonNames[0].CertificateCommonName = $serverCertificateCommonName;
|
|
|
|
# Find certificates for the cluster.
|
|
$adminCert = Find-CertificateByName -CommonName $serverCertificateCommonName -StoreLocation LocalMachine -StoreName "My";
|
|
if($null -eq $adminCert) {
|
|
throw "$loglead : Could not locate server certificate $serverCertificateCommonName. Please make sure that it exists.";
|
|
}
|
|
$clientCert = Find-CertificateByName -CommonName $clientCertificateCommonName -StoreLocation LocalMachine -StoreName "My";
|
|
if($null -eq $clientCert) {
|
|
throw "$loglead : Could not locate server certificate $serverCertificateCommonName. Please make sure that it exists.";
|
|
}
|
|
|
|
Write-Host "$loglead : Fetching issuing certificate thumbprints for admin/client certificates."
|
|
|
|
# Find the thumbprint of the issuing certificate for the admin/client certificates.
|
|
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain;
|
|
|
|
# Find the issuing thumbprint of the admin cert.
|
|
$chain.Build($adminCert);
|
|
$adminIssuerThumbprint = $chain.ChainElements[1].Certificate.Thumbprint;
|
|
$chain.Reset();
|
|
|
|
# Find the issuing thumbprint of the client cert.
|
|
$chain.Build($clientCert);
|
|
$clientIssuerThumbprint = $chain.ChainElements[1].Certificate.Thumbprint;
|
|
$chain.Reset();
|
|
|
|
# Configure admin cert.
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[0].CertificateCommonName = $serverCertificateCommonName;
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[0].CertificateIssuerThumbprint = $adminIssuerThumbprint;
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[0].IsAdmin = $true;
|
|
|
|
# Configure client cert.
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[1].CertificateCommonName = $clientCertificateCommonName;
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[1].CertificateIssuerThumbprint = $clientIssuerThumbprint;
|
|
$config.properties.security.CertificateInformation.ClientCertificateCommonNames[1].IsAdmin = $false;
|
|
|
|
$clusterConfigLocation = "$fabricBasePath\ClusterConfig.json";
|
|
Write-Host "$loglead : Saving Service Fabric cluster definition to $clusterConfigLocation";
|
|
Set-Content -Path $clusterConfigLocation -Value ($config | ConvertTo-Json -Depth 8);
|
|
|
|
Write-Host "$loglead : Starting Remote Registry service on all machines.";
|
|
$script = {
|
|
param($loglead)
|
|
|
|
$server = $env:COMPUTERNAME;
|
|
$serviceName = "RemoteRegistry";
|
|
$service = (Get-Service $serviceName);
|
|
|
|
if($null -eq $service) {
|
|
return;
|
|
}
|
|
|
|
$enabled = ($service.StartType -ne "Disabled");
|
|
if(!$enabled) {
|
|
Write-Host "$loglead : Setting $serviceName to manual on $server";
|
|
Set-Service $serviceName -StartupType Manual;
|
|
}
|
|
|
|
$running = $service.Status -eq "Running"
|
|
if(!$running) {
|
|
Write-Host "$loglead : Starting $serviceName on $server";
|
|
Start-Service $serviceName;
|
|
}
|
|
}
|
|
Invoke-Command -ComputerName $servers -ScriptBlock $script -ArgumentList $loglead;
|
|
|
|
$installerPath = (Join-Path $fabricBasePath "CreateServiceFabricCluster.ps1");
|
|
|
|
Write-Host "$loglead : Creating cluster.";
|
|
Write-Host "$loglead : InstallerScriptPath: $installerPath";
|
|
Write-Host "$loglead : ClusterConfigPath: $clusterConfigLocation";
|
|
Write-Host "$loglead : FabricRuntimePath: $runtimePath";
|
|
& $installerPath -ClusterConfigFilePath $clusterConfigLocation -FabricRuntimePackagePath $runtimePath -AcceptEULA -Verbose;
|
|
} |