scripts/autoscaling/init_multicloud.ps1 (484 lines of code) (raw):

function OpenPortIfRequired($Port) { $FilewallRuleName = "Cloud Pipeline Inbound $Port Port" try { Get-NetFirewallRule -DisplayName $FilewallRuleName -ErrorAction Stop } catch { Write-Host "Opening port $Port..." New-NetFirewallRule -DisplayName $FilewallRuleName ` -Direction Inbound ` -LocalPort $Port ` -Protocol TCP ` -Action Allow } } function NewDirIfRequired($Path) { if (-not(Test-Path $Path)) { New-Item -Path $Path -ItemType "Directory" -Force } } function InitializeDisks { Get-Disk ` | Where-Object { $_.PartitionStyle -eq "raw" } ` | Initialize-Disk -PartitionStyle MBR -PassThru ` | New-Partition -AssignDriveLetter -UseMaximumSize ` | Format-Volume -FileSystem NTFS -Confirm:$false ` | ForEach-Object { $_.DriveLetter + ":" } ` | ForEach-Object { AllowRegularUsersAccess -Path $_ } } function InstallNoMachineIfRequired($GlobalDistributionUrl) { $restartRequired = $false $nomachineInstalled = Get-Service -Name nxservice ` | Measure-Object ` | ForEach-Object { $_.Count -gt 0 } if (-not($nomachineInstalled)) { Write-Host "Installing NoMachine..." Invoke-WebRequest "${GlobalDistributionUrl}tools/nomachine/nomachine_7.6.2_4.exe" -Outfile .\nomachine.exe cmd /c "nomachine.exe /verysilent" @" VirtualDesktopAuthorization 0 PhysicalDesktopAuthorization 0 AutomaticDisconnection 0 ConnectionsLimit 1 ConnectionsUserLimit 1 "@ | Out-File -FilePath "C:\Program Files (x86)\NoMachine\etc\server.cfg" -Encoding ascii -Force -Append $restartRequired=$true } return $restartRequired } function InstallOpenSshServerIfRequired { $restartRequired = $false $openSshServerInstalled = Get-WindowsCapability -Online ` | Where-Object { $_.Name -match "OpenSSH\.Server*" } ` | ForEach-Object { $_.State -eq "Installed" } if (-not($openSshServerInstalled)) { Write-Host "Installing OpenSSH server..." Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force $restartRequired=$false } return $restartRequired } function InstallWebDAVIfRequired { $restartRequired = $false $webDAVInstalled = Get-WindowsFeature ` | Where-Object { $_.Name -match "WebDAV-Redirector" } ` | ForEach-Object { $_.InstallState -eq "Installed" } if (-not($webDAVInstalled)) { Write-Host "Installing WebDAV..." Install-WindowsFeature WebDAV-Redirector $restartRequired=$true } return $restartRequired } function InstallPGinaIfRequired($GlobalDistributionUrl) { $restartRequired = $false $pGinaInstalled = Get-Service -Name pgina ` | Measure-Object ` | ForEach-Object { $_.Count -gt 0 } if (-not($pGinaInstalled)) { Write-Host "Installing pGina..." Invoke-WebRequest "${GlobalDistributionUrl}tools/pgina/pGina-3.2.4.0-setup.exe" -OutFile "pGina-3.2.4.0-setup.exe" Invoke-WebRequest "${GlobalDistributionUrl}tools/pgina/vcredist_x64.exe" -OutFile "vcredist_x64.exe" .\pGina-3.2.4.0-setup.exe /S /D=C:\Program Files\pGina WaitForProcess -ProcessName "pGina-3.2.4.0-setup" .\vcredist_x64.exe /quiet WaitForProcess -ProcessName "vcredist_x64" Invoke-WebRequest "${GlobalDistributionUrl}tools/pgina/pGina.Plugin.AuthenticateAllPlugin.dll" -OutFile "C:\Program Files\pGina\Plugins\Contrib\pGina.Plugin.AuthenticateAllPlugin.dll" Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{d0befefb-3d2c-44da-bbad-3b2d04557246}" -Name "Disabled" -Type "DWord" -Value "1" Set-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{d0befefb-3d2c-44da-bbad-3b2d04557246}" -Name "Disabled" -Type "DWord" -Value "1" $restartRequired=$true } return $restartRequired } function InstallDockerIfRequired { $restartRequired = $false $dockerInstalled = Get-Service -Name docker ` | Measure-Object ` | ForEach-Object { $_.Count -gt 0 } if (-not ($dockerInstalled)) { Get-PackageProvider -Name NuGet -ForceBootstrap Install-Module -Name DockerMsftProvider -Repository PSGallery -Force Install-Package -Name docker -ProviderName DockerMsftProvider -Force -RequiredVersion 19.03.14 $restartRequired=$true } return $restartRequired } function StartOpenSSHServices { Set-Service -Name sshd -StartupType Automatic Start-Service sshd } function StartWebDAVServices { Set-Service WebClient -StartupType Automatic Set-Service MRxDAV -StartupType Automatic Start-Service WebClient Start-Service MRxDAV } function WaitForProcess($ProcessName) { while ($True) { $Process = Get-Process | Where-Object {$_.Name -contains $ProcessName} If ($Process) { Start-Sleep -Seconds 1 } else { break } } } function InstallPythonIfRequired($PythonDir, $GlobalDistributionUrl) { if (-not(Test-Path "$PythonDir")) { Write-Host "Installing python..." Invoke-WebRequest -Uri "${GlobalDistributionUrl}tools/python/3/python-3.8.9-amd64.exe" -OutFile "$workingDir\python-3.8.9-amd64.exe" & "$workingDir\python-3.8.9-amd64.exe" /quiet TargetDir=$PythonDir InstallAllUsers=1 PrependPath=1 WaitForProcess -ProcessName "python-3.8.9-amd64" } } function InstallChromeIfRequired($GlobalDistributionUrl) { if (-not(Test-Path "C:\Program Files\Google\Chrome\Application\chrome.exe")) { Write-Host "Installing chrome..." Invoke-WebRequest "${GlobalDistributionUrl}tools/chrome/ChromeSetup.exe" -Outfile "$workingDir\ChromeSetup.exe" & $workingDir\ChromeSetup.exe /silent /install WaitForProcess -ProcessName "ChromeSetup" } } function InstallDokanyIfRequired($DokanyDir, $GlobalDistributionUrl) { if (-not (Test-Path "$DokanyDir")) { Write-Host "Installing Dokany..." Invoke-WebRequest "${GlobalDistributionUrl}tools/dokany/DokanSetup.exe" -OutFile "$workingDir\DokanSetup.exe" & "$workingDir\DokanSetup.exe" /quiet /silent /verysilent WaitForProcess -ProcessName "DokanSetup" } } function InstallNiceDcvIfRequired { $niceDcvInstalled = Get-Service -Name "DCV Server" ` | Measure-Object ` | ForEach-Object { $_.Count -gt 0 } if (-not ($niceDcvInstalled)) { Invoke-WebRequest "https://d1uj6qtbmh3dt5.cloudfront.net/2021.2/Servers/nice-dcv-server-x64-Release-2021.2-11048.msi" -outfile "$workingDir\nice-dcv-server-x64-Release-2021.2-11048.msi" Start-Process -FilePath "$workingDir\nice-dcv-server-x64-Release-2021.2-11048.msi" -ArgumentList "ADDLOCAL=ALL /quiet /norestart /l*v $workingDir\nice_dcv_install.log" -Wait -PassThru } } function GenerateSshKeys($Path) { NewDirIfRequired -Path "$Path\.ssh" if (!(Test-Path "$Path\.ssh\id_rsa")) { cmd /c "ssh-keygen.exe -t rsa -N """" -f ""$Path\.ssh\id_rsa""" } } function AddPublicKeyToAuthorizedKeys($SourcePath, $DestinationPath) { NewDirIfRequired -Path (Split-Path -Path $DestinationPath) Get-Content $SourcePath | Out-File -FilePath $DestinationPath -Encoding ascii -Force RestrictRegularUsersAccess -Path $DestinationPath } function CopyPrivateKey($SourcePath, $DestinationPath) { Copy-Item -Path (Split-Path -Path $SourcePath) -Destination (Split-Path -Path $DestinationPath) -Recurse RestrictRegularUsersAccess -Path $DestinationPath } function AllowRegularUsersAccess($Path) { $acl = Get-Acl $Path $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Users", "Write", "ContainerInherit,ObjectInherit", "None", "Allow") $acl.AddAccessRule($rule) $acl | Set-Acl $Path } function RestrictRegularUsersAccess($Path) { $acl = Get-Acl $Path $rules = $acl.Access ` | Where-Object { $_.IdentityReference -in "NT AUTHORITY\SYSTEM","BUILTIN\Administrators" } $acl.SetAccessRuleProtection($true, $false) $rules | ForEach-Object { $acl.AddAccessRule($_) } $acl | Set-Acl $Path } function ConfigureAndRestartDockerDaemon { $dockerdaemonconfigfile = @" { "insecure-registries" : ["cp-docker-registry.default.svc.cluster.local:31443"], "allow-nondistributable-artifacts": ["cp-docker-registry.default.svc.cluster.local:31443"] } "@ $dockerdaemonconfigfile|Out-File -FilePath C:\ProgramData\docker\config\daemon.json -Encoding ascii -Force Restart-Service docker -Force } function DownloadSigWindowsToolsIfRequired($GlobalDistributionUrl) { if (-not(Test-Path .\sig-windows-tools)) { NewDirIfRequired -Path .\sig-windows-tools Invoke-WebRequest "${GlobalDistributionUrl}tools/kube/1.15.4/win/sig-windows-tools-00012ee6d171b105e7009bff8b2e42d96a45426f.zip" -Outfile .\sig-windows-tools.zip tar -xvf .\sig-windows-tools.zip --strip-components=1 -C sig-windows-tools } } function PatchSigWindowsTools($KubeHost, $KubePort, $Dns) { $instanceId=$(Invoke-RestMethod -uri http://169.254.169.254/latest/meta-data/instance-id) $kubeClusterHelperContent = Get-Content .\sig-windows-tools\kubeadm\KubeClusterHelper.psm1 $kubeClusterHelperContent[262] = ' Write-Host "Skipping node joining verification..."' $kubeClusterHelperContent[263] = ' return 1' $kubeClusterHelperContent[344] = ' $nodeName = "' + $instanceId + '"' $kubeClusterHelperContent[656] = ' "--hostname-override=' + $instanceId + '"' $kubeClusterHelperContent[704] = ' "--hostname-override=' + $instanceId + '"' $kubeClusterHelperContent[723] = ' hostnameOverride = "' + $instanceId + '";' $kubeClusterHelperContent[778] = ' -BinaryPathName "$kubeletBinPath --windows-service --v=6 --log-dir=$logDir --cert-dir=$env:SYSTEMDRIVE\var\lib\kubelet\pki --cni-bin-dir=$CniDir --cni-conf-dir=$CniConf --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --hostname-override=' + $instanceId + ' --pod-infra-container-image=$Global:PauseImage --enable-debugging-handlers --cgroups-per-qos=false --enforce-node-allocatable=`"`" --logtostderr=false --network-plugin=cni --resolv-conf=`"`" --cluster-dns=`"$KubeDnsServiceIp`" --cluster-domain=cluster.local --feature-gates=$KubeletFeatureGates"' $kubeClusterHelperContent[782] = ' & cmd /c kubeadm join "$(GetAPIServerEndpoint)" --token "$Global:Token" --discovery-token-ca-cert-hash "$Global:CAHash" --ignore-preflight-errors "all" --node-name "' + $instanceId + '" ''2>&1''' $kubeClusterHelperContent[1212] = ' Write-Host "Returning kubernetes dns ip..."' $kubeClusterHelperContent[1213] = " return '$Dns'" $kubeClusterHelperContent[1217] = ' Write-Host "Returning kubernetes master address..."' $kubeClusterHelperContent[1218] = " return '$KubeHost`:$KubePort'" $kubeClusterHelperContent[1223] = ' Write-Host "Skipping nodes listing..."' $kubeClusterHelperContent[1228] = ' Write-Host "Skipping node deletion..."' $kubeClusterHelperContent | Set-Content .\sig-windows-tools\kubeadm\KubeClusterHelper.psm1 } function InitSigWindowsToolsConfigFile($KubeHost, $KubeToken, $KubeCertHash, $KubeDir, $Interface, $GlobalDistributionUrl) { $configfile = @" { "Cri" : { "Name" : "dockerd", "Images" : { "Pause" : "mcr.microsoft.com/k8s/core/pause:1.2.0", "Nanoserver" : "mcr.microsoft.com/windows/nanoserver:1809", "ServerCore" : "mcr.microsoft.com/windows/servercore:ltsc2019" } }, "Cni" : { "Name" : "flannel", "Source" : [{ "Name" : "flanneld", "Url" : "${GlobalDistributionUrl}tools/kube/1.15.4/win/flanneld.exe" } ], "Plugin" : { "Name": "vxlan" }, "InterfaceName" : "$Interface" }, "Kubernetes" : { "Source" : { "Release" : "1.15.4", "Url" : "${GlobalDistributionUrl}tools/kube/1.15.4/win/kubernetes-node-windows-amd64.tar.gz" }, "ControlPlane" : { "IpAddress" : "$KubeHost", "Username" : "root", "KubeadmToken" : "$KubeToken", "KubeadmCAHash" : "sha256:$KubeCertHash" }, "KubeProxy" : { "Gates" : "WinOverlay=true" }, "Network" : { "ServiceCidr" : "10.96.0.0/12", "ClusterCidr" : "10.244.0.0/16" } }, "Install" : { "Destination" : "$($KubeDir -replace "\\","\\")" } } "@ $configfile|Out-File -FilePath .\Kubeclustervxlan.json -Encoding ascii -Force RestrictRegularUsersAccess -Path .\Kubeclustervxlan.json } function InstallKubeUsingSigWindowsToolsIfRequired($KubeDir) { $kubernetesInstalled = Get-ChildItem $KubeDir ` | Measure-Object ` | ForEach-Object { $_.Count -gt 0 } if (-not($kubernetesInstalled)) { Write-Host "Installing kubernetes using Sig Windows Tools..." .\sig-windows-tools\kubeadm\KubeCluster.ps1 -ConfigFile .\Kubeclustervxlan.json -install } } function WriteKubeConfig($KubeHost, $KubePort, $KubeNodeToken, $KubeDir) { $kubeConfig = @" apiVersion: v1 kind: Config preferences: {} clusters: - cluster: insecure-skip-tls-verify: true server: https://$KubeHost`:$KubePort name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-user name: kubernetes-user@kubernetes users: - name: kubernetes-user user: token: $KubeNodeToken current-context: kubernetes-user@kubernetes "@ $kubeConfig | Out-File -FilePath "$KubeDir\config" -Encoding ascii -Force RestrictRegularUsersAccess -Path "$KubeDir\config" } function JoinKubeClusterUsingSigWindowsTools { .\sig-windows-tools\kubeadm\KubeCluster.ps1 -ConfigFile .\Kubeclustervxlan.json -join } function WaitAndConfigureDnsIfRequired($Dns, $Interface) { if (-not([string]::IsNullOrEmpty($Dns))) { while ($true) { try { Resolve-DnsName kubernetes.default.svc.cluster.local -Server $Dns -QuickTimeout -ErrorAction Stop break } catch { Write-Host "Still waiting for dns at $Dns..." } } Write-Host "Adding dns to network interface..." $interfaceIndex = Get-NetAdapter "$Interface" | ForEach-Object { $_.ifIndex } Set-DnsClientServerAddress -InterfaceIndex $interfaceIndex -ServerAddresses ("$Dns") } } function ConfigureAwsRoutes($Addrs, $Interface) { # See C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Scripts\Add-Routes.ps1 $interfaceIndex = Get-NetAdapter $Interface | ForEach-Object { $_.ifIndex } $networkAdapterConfig = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "InterfaceIndex='$interfaceIndex'" ` | Select-Object IPConnectionMetric, DefaultIPGateway foreach ($addr in $Addrs) { Remove-NetRoute -DestinationPrefix $addr -PolicyStore ActiveStore -Confirm:$false -ErrorAction SilentlyContinue Remove-NetRoute -DestinationPrefix $addr -PolicyStore PersistentStore -Confirm:$false -ErrorAction SilentlyContinue New-NetRoute -DestinationPrefix $addr -InterfaceIndex $interfaceIndex ` -NextHop ([string] $networkAdapterConfig.DefaultIPGateway) -RouteMetric $networkAdapterConfig.IPConnectionMetric -ErrorAction Stop } w32tm /resync /rediscover /nowait } function ConfigureLoopbackRouteIfEnabled($Preferences, $Interface) { $loopbackRouteEnabled = $Preferences ` | Where-Object { $_.Name -eq "cluster.windows.node.loopback.route" } ` | ForEach-Object { $_.Value -eq "true" } if ($loopbackRouteEnabled) { Write-Host "Configuring loopback route..." $interfaceIndex = Get-NetAdapter $Interface | ForEach-Object { $_.ifIndex } $interfaceIp = Get-NetIPAddress -AddressFamily IPv4 -InterfaceIndex $interfaceIndex | Select-Object -ExpandProperty IPAddress $Addrs = @("$interfaceIp/32") ConfigureAwsRoutes -Addrs $Addrs -Interface $Interface } } function LoadPreferences($ApiUrl, $ApiToken) { return RequestApi -ApiUrl $ApiUrl -ApiToken $ApiToken -HttpMethod "GET" -ApiMethod "preferences" } function RequestApi($ApiUrl, $ApiToken, $HttpMethod, $ApiMethod, $Body = $null) { Write-Host "Requesting ${ApiUrl}${ApiMethod}..." $Response = Invoke-RestMethod -Method $HttpMethod ` -Uri "${ApiUrl}${ApiMethod}" ` -Body $Body ` -Headers @{ "Authorization" = "Bearer $ApiToken" "Content-Type" = "application/json" } if ($Response.status -ne "OK") { Write-Error $Response.message return $null } return $Response.payload } function ListenForConnection($Port) { $endpoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any, $Port) $listener = New-Object System.Net.Sockets.TcpListener $endpoint $listener.Start() $client = $listener.AcceptTcpClient() $stream = $client.GetStream(); $reader = New-Object System.IO.StreamReader $stream do { $line = $reader.ReadLine() Write-Host $line -fore cyan } while ($line -and $line -ne ([char]4)) $reader.Dispose() $stream.Dispose() $client.Dispose() $listener.Stop() } $apiUrl = "@API_URL@" $apiToken = "@API_TOKEN@" $kubeAddress = "@KUBE_IP@" $kubeHost, $kubePort = $kubeAddress.split(":",2) $kubeToken = "@KUBE_TOKEN@" $kubeCertHash = "@KUBE_CERT_HASH@" $kubeNodeToken = "@KUBE_NODE_TOKEN@" $dnsProxyPost = "@dns_proxy_post@" $globalDistributionUrl = "@GLOBAL_DISTRIBUTION_URL@" $interface = Get-NetAdapter | Where-Object { $_.Name -match "Ethernet \d+" } | ForEach-Object { $_.Name } $interfacePost = "vEthernet ($interface)" $awsAddrs = @("169.254.169.254/32", "169.254.169.250/32", "169.254.169.251/32", "169.254.169.249/32", "169.254.169.123/32", "169.254.169.253/32") $homeDir = "$env:USERPROFILE" $workingDir = "c:\init" $hostDir = "c:\host" $runsDir = "c:\runs" $kubeDir = "c:\ProgramData\Kubernetes" $pythonDir = "c:\python" $dokanyDir = "C:\Program Files\Dokan\Dokan Library-1.5.0" $initLog = "$workingDir\log.txt" Write-Host "Creating system directories..." NewDirIfRequired -Path $workingDir NewDirIfRequired -Path $hostDir NewDirIfRequired -Path $runsDir NewDirIfRequired -Path $kubeDir Write-Host "Starting logs capturing..." Start-Transcript -path $initLog -append Write-Host "Changing working directory..." Set-Location -Path "$workingDir" Write-Host "Initializing disks..." InitializeDisks $restartRequired = $false Write-Host "Installing nomachine if required..." $restartRequired = (InstallNoMachineIfRequired -GlobalDistributionUrl $globalDistributionUrl | Select-Object -Last 1) -or $restartRequired Write-Host "Restart required: $restartRequired" Write-Host "Installing OpenSSH server if required..." $restartRequired = (InstallOpenSshServerIfRequired | Select-Object -Last 1) -or $restartRequired Write-Host "Restart required: $restartRequired" Write-Host "Installing WebDAV if required..." $restartRequired = (InstallWebDAVIfRequired | Select-Object -Last 1) -or $restartRequired Write-Host "Restart required: $restartRequired" Write-Host "Installing pGina if required..." $restartRequired = (InstallPGinaIfRequired -GlobalDistributionUrl $globalDistributionUrl | Select-Object -Last 1) -or $restartRequired Write-Host "Restart required: $restartRequired" Write-Host "Installing docker if required..." $restartRequired = (InstallDockerIfRequired | Select-Object -Last 1) -or $restartRequired Write-Host "Restart required: $restartRequired" Write-Host "Restarting computer if required..." if ($restartRequired) { Write-Host "Restarting computer..." Stop-Transcript Restart-Computer -Force Exit } Write-Host "Starting OpenSSH services..." StartOpenSSHServices Write-Host "Starting WebDAV services..." StartWebDAVServices Write-Host "Installing python if required..." InstallPythonIfRequired -PythonDir $pythonDir -GlobalDistributionUrl $globalDistributionUrl Write-Host "Installing chrome if required..." InstallChromeIfRequired -GlobalDistributionUrl $globalDistributionUrl Write-Host "Installing Dokany if required..." InstallDokanyIfRequired -DokanyDir $dokanyDir -GlobalDistributionUrl $globalDistributionUrl Write-Host "Installing NICE DCV if required..." InstallNiceDcvIfRequired Write-Host "Opening host ports..." OpenPortIfRequired -Port 4000 OpenPortIfRequired -Port 8888 Write-Host "Generating SSH keys..." GenerateSshKeys -Path $homeDir Write-Host "Adding node SSH keys to authorized keys..." AddPublicKeyToAuthorizedKeys -SourcePath "$homeDir\.ssh\id_rsa.pub" -DestinationPath C:\Windows\.ssh\authorized_keys AddPublicKeyToAuthorizedKeys -SourcePath "$homeDir\.ssh\id_rsa.pub" -DestinationPath C:\ProgramData\ssh\administrators_authorized_keys Write-Host "Publishing node SSH keys..." CopyPrivateKey -SourcePath "$homeDir\.ssh\id_rsa" -DestinationPath "$hostDir\.ssh\id_rsa" Write-Host "Configuring docker daemon..." ConfigureAndRestartDockerDaemon Write-Host "Downloading Sig Windows Tools if required..." DownloadSigWindowsToolsIfRequired -GlobalDistributionUrl $globalDistributionUrl Write-Host "Patching KubeClusterHelper.psm1 script to ignore all preflight errors..." PatchSigWindowsTools -KubeHost $kubeHost -KubePort $kubePort -Dns $dnsProxyPost Write-Host "Generating Sig Windows Tools config file..." InitSigWindowsToolsConfigFile -KubeHost $kubeHost -KubeToken $kubeToken -KubeCertHash $kubeCertHash -KubeDir $kubeDir -Interface $interface -GlobalDistributionUrl $globalDistributionUrl Write-Host "Installing kubernetes using Sig Windows Tools if required..." InstallKubeUsingSigWindowsToolsIfRequired -KubeDir $kubeDir Write-Host "Writing kubernetes config..." WriteKubeConfig -KubeHost $kubeHost -KubePort $kubePort -KubeNodeToken $kubeNodeToken -KubeDir $kubeDir Write-Host "Joining kubernetes cluster using Sig Windows Tools..." JoinKubeClusterUsingSigWindowsTools Write-Host "Configuring AWS routes..." ConfigureAwsRoutes -Addrs $awsAddrs -Interface $interfacePost Write-Host "Waiting for dns to be accessible if required..." WaitAndConfigureDnsIfRequired -Dns $dnsProxyPost -Interface $interfacePost Write-Host "Loading preferences..." $preferences = LoadPreferences -ApiUrl $apiUrl -ApiToken $apiToken Write-Host "Configuring loopback route if it is enabled in preferences..." ConfigureLoopbackRouteIfEnabled -Preferences $preferences -Interface $interfacePost Write-Host "Listening on port 8888..." ListenForConnection -Port 8888 Stop-Transcript