<# Copyright 2021 VMware, Inc. SPDX-License-Identifier: BSD-2-Clause #> Function Get-VCHAConfig { <# .NOTES =========================================================================== Created by: William Lam Date: Nov 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function retrieves the VCHA Configuration which provides you with the current state, mode as well as the IP Addresses of the Active, Passive & Witness Node. This is only available on VCSA 6.5 (vSphere 6.5 or greater) .DESCRIPTION Function to return VCHA Configuration .EXAMPLE Get-VCHAConfig #> $vcHAClusterConfig = Get-View failoverClusterConfigurator $vcHAConfig = $vcHAClusterConfig.getVchaConfig() $vcHAState = $vcHAConfig.State switch($vcHAState) { configured { $activeIp = $vcHAConfig.FailoverNodeInfo1.ClusterIpSettings.Ip.IpAddress $passiveIp = $vcHAConfig.FailoverNodeInfo2.ClusterIpSettings.Ip.IpAddress $witnessIp = $vcHAConfig.WitnessNodeInfo.IpSettings.Ip.IpAddress $vcHAClusterManager = Get-View failoverClusterManager $vcHAMode = $vcHAClusterManager.getClusterMode() Write-Host "" Write-Host -NoNewline -ForegroundColor Green "VCHA State: " Write-Host -ForegroundColor White "$vcHAState" Write-Host -NoNewline -ForegroundColor Green " VCHA Mode: " Write-Host -ForegroundColor White "$vcHAMode" Write-Host -NoNewline -ForegroundColor Green " ActiveIP: " Write-Host -ForegroundColor White "$activeIp" Write-Host -NoNewline -ForegroundColor Green " PassiveIP: " Write-Host -ForegroundColor White "$passiveIp" Write-Host -NoNewline -ForegroundColor Green " WitnessIP: " Write-Host -ForegroundColor White "$witnessIp`n" ;break } invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} notConfigured { Write-Host "VCHA is not configured";break} prepared { Write-Host "VCHA is being prepared, please try again in a little bit ...";break} } } Function Get-VCHAClusterHealth { <# .NOTES =========================================================================== Created by: William Lam Date: Nov 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function retrieves the VCHA Cluster Health which provides more info on each of the individual. This is only available on VCSA 6.5 (vSphere 6.5 or greater) .DESCRIPTION Function to return VCHA Cluster Health .EXAMPLE Get-VCHAClusterHealth #> $vcHAClusterConfig = Get-View failoverClusterConfigurator $vcHAConfig = $vcHAClusterConfig.getVchaConfig() $vcHAState = $vcHAConfig.State switch($vcHAState) { invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} notConfigured { Write-Host "VCHA is not configured";break} prepared { Write-Host "VCHA is being prepared ...";break} configured { $vcHAClusterManager = Get-View failoverClusterManager $healthInfo = $vcHAClusterManager.GetVchaClusterHealth() $vcClusterState = $healthInfo.RuntimeInfo.ClusterState $nodeState = $healthInfo.RuntimeInfo.NodeInfo Write-Host "" Write-Host -NoNewline -ForegroundColor Green "VCHA Cluster State: " Write-Host -ForegroundColor White "$vcClusterState" Write-Host -NoNewline -ForegroundColor Green "VCHA Node Information: " $nodeState | Select NodeIp, NodeRole, NodeState ;break } } } Function Set-VCHAClusterMode { <# .NOTES =========================================================================== Created by: William Lam Date: Nov 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function allows you to set the mode of the VCHA Cluster whether that is Enabled, Disabled or in Maintenance Mode. This is only available on VCSA 6.5 (vSphere 6.5 or greater) .DESCRIPTION Function to set VCHA Cluster Mode .EXAMPLE Set-VCHAClusterMode -Enabled $true .EXAMPLE Set-VCHAClusterMode -Disabled $true .EXAMPLE Set-VCHAClusterMode -Maintenance $true #> param( [Switch]$Enabled, [Switch]$Disabled, [Switch]$Maintenance ) $vcHAClusterManager = Get-View failoverClusterManager if($Enabled) { Write-Host "Setting VCHA Cluster to Enabled ..." $task = $vcHAClusterManager.setClusterMode_Task("enabled") $task1 = Get-Task -Id ("Task-$($task.value)") $task1 | Wait-Task } elseIf($Maintenance) { Write-Host "Setting VCHA Cluster to Maintenance ..." $task = $vcHAClusterManager.setClusterMode_Task("maintenance") $task1 = Get-Task -Id ("Task-$($task.value)") $task1 | Wait-Task } elseIf($Disabled) { Write-Host "`nSetting VCHA Cluster to Disabled ...`n" $task = $vcHAClusterManager.setClusterMode_Task("disabled") $task1 = Get-Task -Id ("Task-$($task.value)") $task1 | Wait-Task } } Function New-VCHABasicConfig { <# .NOTES =========================================================================== Created by: William Lam Date: Nov 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function allows you create a new "Basic" VCHA Cluster, it does not cover the "Advanced" use case. You will need to ensure that you have a "Self Managed" vCenter Server before attempting this workflow. This is only available on VCSA 6.5 (vSphere 6.5 or greater) .DESCRIPTION Function to create "Basic" VCHA Cluster .PARAMETER VCSAVM The name of the vCenter Server Appliance (VCSA) in which you wish to enable VCHA on (must be self-managed) .PARAMETER HANetwork The name of the Virtual Portgroup or Distributed Portgroup used for the HA Network .PARAMETER ActiveHAIp The IP Address for the Active VCSA node .PARAMETER ActiveNetmask The Netmask for the Active VCSA node .PARAMETER PassiveHAIp The IP Address for the Passive VCSA node .PARAMETER PassiveNetmask The Netmask for the Passive VCSA node .PARAMETER WitnessHAIp The IP Address for the Witness VCSA node .PARAMETER WitnessNetmask The Netmask for the Witness VCSA node .PARAMETER PassiveDatastore The name of the datastore to deploy the Passive node to .PARAMETER WitnessDatastore The name of the datastore to deploy the Witness node to .PARAMETER VCUsername The VCSA username (e.g. administrator@vghetto.local) .PARAMETER VCPassword The VCSA password .EXAMPLE New-VCHABasicConfig -VCSAVM "vcenter65-1" -HANetwork "DVPG-VCHA-Network" ` -ActiveHAIp 192.168.1.70 ` -ActiveNetmask 255.255.255.0 ` -PassiveHAIp 192.168.1.71 ` -PassiveNetmask 255.255.255.0 ` -WitnessHAIp 192.168.1.72 ` -WitnessNetmask 255.255.255.0 ` -PassiveDatastore "vsanDatastore" ` -WitnessDatastore "vsanDatastore" ` -VCUsername "administrator@vghetto.local" ` -VCPassword "VMware1!" #> param( [Parameter( Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true) ] [String]$VCSAVM, [String]$HANetwork, [String]$ActiveHAIp, [String]$ActiveNetmask, [String]$PassiveHAIp, [String]$PassiveNetmask, [String]$PassiveDatastore, [String]$WitnessHAIp, [String]$WitnessNetmask, [String]$WitnessDatastore, # Crappy Implementation but need to research more into using PSH Credential [String]$VCUsername, [String]$VCPassword ) $VCSAVMView = Get-View -ViewType VirtualMachine -Filter @{"name"=$VCSAVM} if($VCSAVMView -eq $null) { Write-Host -ForegroundColor Red "Error: Unable to find Virtual Machine $VCSAVM" return } $HANetworkView = Get-View -ViewType Network -Filter @{"name"=$HANetwork} if($HANetworkView -eq $null) { Write-Host -ForegroundColor Red "Error: Unable to find Network $HANetwork" return } $PassiveDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$PassiveDatastore} if($PassiveDatastoreView -eq $null) { Write-Host -ForegroundColor Red "Error: Unable to find Passive Datastore $PassiveDatastore" return } $WitnessDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$WitnessDatastore} if($WitnessDatastoreView -eq $null) { Write-Host -ForegroundColor Red "Error: Unable to find Witness Datastore $WitnessDatastore" return } $vcIP = $VCSAVMView.Guest.IpAddress if($vcIP -eq $null) { Write-Host -ForegroundColor Red "Error: Unable to automatically retrieve the IP Address of $VCSAVM which is needed to use this function" return } # Retrieve Source VC SSL Thumbprint $vcurl = "https://$vcIP" add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class IDontCarePolicy : ICertificatePolicy { public IDontCarePolicy() {} public bool CheckValidationResult( ServicePoint sPoint, X509Certificate cert, WebRequest wRequest, int certProb) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy # Need to do simple GET connection for this method to work Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") # Get Thumbprint + add colons for a valid Thumbprint $vcSSLThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' $vcHAClusterConfig = Get-View failoverClusterConfigurator $spec = New-Object VMware.Vim.VchaClusterDeploymentSpec $activeNetworkConfig = New-Object VMware.Vim.ClusterNetworkConfigSpec $activeNetworkConfig.NetworkPortGroup = $HANetworkView.MoRef $ipSettings = New-Object Vmware.Vim.CustomizationIPSettings $ipSettings.SubnetMask = $ActiveNetmask $activeIpSpec = New-Object VMware.Vim.CustomizationFixedIp $activeIpSpec.IpAddress = $ActiveHAIp $ipSettings.Ip = $activeIpSpec $activeNetworkConfig.IpSettings = $ipSettings $spec.ActiveVcNetworkConfig = $activeNetworkConfig $activeVCConfig = New-Object Vmware.Vim.SourceNodeSpec $activeVCConfig.ActiveVc = $VCSAVMView.MoRef $serviceLocator = New-Object Vmware.Vim.ServiceLocator $credential = New-Object VMware.Vim.ServiceLocatorNamePassword $credential.username = $VCUsername $credential.password = $VCPassword $serviceLocator.Credential = $credential $serviceLocator.InstanceUuid = $global:DefaultVIServer.InstanceUuid $serviceLocator.Url = $vcurl $serviceLocator.SslThumbprint = $vcSSLThumbprint $activeVCConfig.ManagementVc = $serviceLocator $spec.ActiveVcSpec = $activeVCConfig $passiveSpec = New-Object VMware.Vim.PassiveNodeDeploymentSpec $passiveSpec.Folder = (Get-View (Get-Folder vm)).MoRef $passiveIpSettings = New-object Vmware.Vim.CustomizationIPSettings $passiveIpSettings.SubnetMask = $passiveNetmask $passiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp $passiveIpSpec.IpAddress = $passiveHAIp $passiveIpSettings.Ip = $passiveIpSpec $passiveSpec.IpSettings = $passiveIpSettings $passiveSpec.NodeName = $VCSAVMView.Name + "-Passive" $passiveSpec.datastore = $PassiveDatastoreView.MoRef $spec.PassiveDeploymentSpec = $passiveSpec $witnessSpec = New-Object VMware.Vim.NodeDeploymentSpec $witnessSpec.Folder = (Get-View (Get-Folder vm)).MoRef $witnessSpec.NodeName = $VCSAVMView.Name + "-Witness" $witnessIpSettings = New-object Vmware.Vim.CustomizationIPSettings $witnessIpSettings.SubnetMask = $witnessNetmask $witnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp $witnessIpSpec.IpAddress = $witnessHAIp $witnessIpSettings.Ip = $witnessIpSpec $witnessSpec.IpSettings = $witnessIpSettings $witnessSpec.datastore = $WitnessDatastoreView.MoRef $spec.WitnessDeploymentSpec = $witnessSpec Write-Host "`nDeploying VCHA Cluster ...`n" $task = $vcHAClusterConfig.deployVcha_Task($spec) $task1 = Get-Task -Id ("Task-$($task.value)") $task1 | Wait-Task -Verbose } Function Remove-VCHAConfig { <# .NOTES =========================================================================== Created by: William Lam Date: Nov 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function allows you destroy a VCHA Cluster. In addition, you have the option to specify whether you would like both the Passive & Witness Virtual Machines be deleted after the VCHA Cluster has been destroyed. This is only available on VCSA 6.5 (vSphere 6.5 or greater) .DESCRIPTION Function to destroy a VCHA Cluster Mode .EXAMPLE Remove-VCHAConfig .EXAMPLE Remove-VCHAConfig -Confirm:$false .EXAMPLE Remove-VCHAConfig -DeleteVM $true -Confirm:$false .NOTES Before you can destroy a VCHA Cluster, you must make sure it is first disabled. Run the Set-VCHAClusterMode -Disabled $true to do so #> param( [Boolean]$Confirm=$true, [Switch]$DeleteVM=$false ) $Verified = $false if($Confirm -eq $true) { Write-Host -ForegroundColor Yellow "`nDo you want to destroy VCHA Cluster?" $answer = Read-Host -Prompt "Do you accept (Y or N)" if($answer -eq "Y" -or $answer -eq "y") { $Verified = $true } } else { $Verified = $true } if($Verified) { $vcHAClusterManager = Get-View failoverClusterManager $vcHAMode = $vcHAClusterManager.getClusterMode() if($vcHAMode -ne "disabled") { Write-Host -ForegroundColor Yellow "To destroy VCHA Cluster, you must first set the VCHA Cluster Mode to `"Disabled`"" Exit } # Query BIOS UUID of the Passive/Witness to be able to delete if($DeleteVM) { $vcHAClusterConfig = Get-View failoverClusterConfigurator $vcHAConfig = $vcHAClusterConfig.getVchaConfig() $passiveBiosUUID = $vcHAConfig.FailoverNodeInfo2.biosUuid $witnessBiosUUID = $vcHAConfig.WitnessNodeInfo.biosUuid } $vcHAClusterConfig = Get-View failoverClusterConfigurator Write-Host "Destroying VCHA Cluster ..." $task = $vcHAClusterConfig.destroyVcha_Task() $task1 = Get-Task -Id ("Task-$($task.value)") $task1 | Wait-Task # After VCHA Cluster has been destroyed, we can now delete the VMs we had queried earlier if($DeleteVM) { if($passiveBiosUUID -ne $null -and $witnessBiosUUID -ne $null) { $searchIndex = Get-View searchIndex $passiveVM = $searchIndex.FindByUuid($null,$passiveBiosUUID,$true,$null) $witnessVM = $searchIndex.FindByUuid($null,$witnessBiosUUID,$true,$null) if($passiveVM -ne $null -and $witnessVM -ne $null) { Write-Host "Powering off & deleting Passive VM ..." Stop-VM -VM (Get-View $passiveVM).Name -Confirm:$false | Out-Null Remove-VM (Get-View $passiveVM).Name -DeletePermanently -Confirm:$false Write-Host "Powering off & deleting Witness VM ..." Stop-VM -VM (Get-View $witnessVM).Name -Confirm:$false | Out-Null Remove-VM (Get-View $witnessVM).Name -DeletePermanently -Confirm:$false } } } } }