PowerShell: Microsoft Failover Cluster Discovery Version 0.10

<#  .Sypnosis: Microsoft_Clusters_Discovery_v.0.10.ps1
What it does:
- Installs Microsoft Failover Cluster PowerShell module on the local machine (Jump Box).
This can work behind a proxy if those variables are specified.
Be advised that RSAT-Clustering-PowerShell currently has known conflicts with VMWare.PowerCLI.
Thus, this must be ran on machines that does not already have VMWare.PowerCLI installed.
- Collects all FailOver Clusters in the current domain
- Lists all nodes of a selected cluster
- Checks whether Remote PowerShell is enabled on a selected node
- Connects to a user-selected node via WinRM and queries for clustering information: resource names and current owners

Requirements:
- Script must be ran using a Domain Admin account
- It can only query remote machines with WinRM being enabled
- Requires a manual trigger of PowerShell Execution Policy: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Confirm:$false -Force

Future development:
- Runs as a Domain Admin
- Uses PSExec to enable WinRM on remote machines prior to executing Remote Powershell invoke-command
- Describes the clustering types and trigger selected functions to match the type of resource (SQL, Hyper-V, FileServer, etc.)
#>

$proxy="http://proxy:8080"
$exclusionList="localhost;*.sdcs.local"

<# Future development: Domain Admin credential prompts
# Credential for PS-Remoting
$usernameInput=Read-Host -Prompt "Input the username";
$username="$($env:USERDNSDOMAIN)\$usernameInput";
$password = Read-Host -Prompt "Input the password for account $username" -AsSecureString
#$password=convertto-securestring "PASSWORD" -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,$password
#>

################################## Excuting Program as an Administrator ####################################
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}

Write-Host -NoNewLine "Running as Administrator..."
################################## Excuting Program as an Administrator ####################################

function checkProxy{
try{
$connectionTest=iwr download.microsoft.com -UseBasicParsing
#$connectionSucceeds=Test-NetConnection -Computername download.microsoft.com -Port 443 -InformationLevel Quiet
if ($connectionTest){
return $True;
}
}
catch{
return $False
}
}

function fixProxy{
# Check if proxy is enabled on the system and fix it
$proxyKey=(Get-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings")
if ($proxyKey.ProxyEnable){
# Set http proxy for browsers
Set-Itemproperty -path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -Name 'ProxyServer' -value $proxy

# Set winhttp proxy for PowerShell
netsh winhttp set proxy $proxy $exclusionList

[system.net.webrequest]::defaultwebproxy = New-Object system.net.webproxy($proxy)
[system.net.webrequest]::defaultwebproxy.credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
[system.net.webrequest]::defaultwebproxy.BypassProxyOnLocal = $true
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}

if (checkProxy){
"Proxy is now good to go..."
}
else{
"Proxy problems..."
break;
}
}

# Set PowerShell Gallery as Trusted to bypass prompts
Function setPSGalleryTrust{
$trustPSGallery=(Get-psrepository -Name 'PSGallery').InstallationPolicy
If($trustPSGallery -ne 'Trusted'){
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
}
}

# Adding Prerequisite Active Directory Module
Function installADModule{
if (!(get-module -name "ActiveDirectory") ){
Add-WindowsFeature RSAT-AD-PowerShell | out-null;
import-module -name "ActiveDirectory" -DisableNameChecking | out-null;
}
$domain=get-addomain
}

# Adding Prerequisite Microsoft Cluster
Function installFailoverClustersModule{
if (!(get-module -Name "FailoverClusters") ){
#Install-WindowsFeature Failover-Clustering | out-null;
Install-WindowsFeature RSAT-Clustering-MGMT | out-null;
Install-WindowsFeature RSAT-Clustering-PowerShell | out-null;
Import-Module FailoverClusters | out-null;
}
}

Function installPrerequisites{
setPSGalleryTrust;
#installADModule;
installFailoverClustersModule;
}

function listClusters{
"`n`nListing all Clusters in $($env:USERDNSDOMAIN)...`n"
$GLOBAL:clusters=get-cluster -domain $env:USERDNSDOMAIN
}

function listNodes{
"`n`nAttempting to collect a list of all Nodes in this Cluster...`n"
try{
$global:nodes=(Get-ClusterNode -Cluster "$pickedItem.$env:USERDNSDOMAIN").Name
$global:pickedCluster=$pickedItem
}
catch{
$_.Exception.Message;
}
#$global:headLine="Available nodes in $pickedItem`:"
}

function showMenu{
Param($callFunction)
invoke-command (Get-Item "function:$callFunction").ScriptBlock

switch($callFunction){
"listClusters"{$count=$clusters.count; $global:list=$clusters;}
"listNodes" {$count=$nodes.count; $global:list=$nodes;}
}

$show="`n--------------------------------------------------------`nThere are $count items on this list:`n--------------------------------------------------------`n"
for ($row=0;$row -le $count-1;$row++){
$server=$list[$row]
$show += "$row" + ": " + "$server" + "`n"
}
return $show
}

function pickItem{
do {
try {
$flag = $true
[int]$pick=Read-Host -Prompt "`n---------------------------------------`nPlease pick an item from the above list`n---------------------------------------"
} # end try
catch {$flag = $false}
} # end do
until ($pick -lt $list.count -and $flag)

$global:pickIndex=$pick
$global:pickedItem=$list[$pick]
$clusters=""
$nodes=""
"$pick`: $pickedItem is picked"
}

# return true or false as determinant on whether program proceeds
function checkNodesForWinRM{
$nodes | foreach {
$winRM=Test-NetConnection -ComputerName "$_`.$($env:USERDNSDOMAIN)" -CommonTCPPort WINRM -InformationLevel Quiet
if ($winRM){"$_ is reachable via WinRM.";}
}
}

function queryFailoverClusters{

# Pick Cluster
do {
try{
$flag1=$true
showMenu -callFunction listClusters
pickItem
}
catch{$flag1=$false}
} until ($flag1)

# Pick Node
do {
try{
$flag2=$true
showMenu -callFunction listNodes
if (!($nodes)){queryFailoverClusters;}
checkNodesForWinRM;
pickItem
}
catch{$flag2=$false}
} until($flag2)
}

$failoverClusterModuleAvailable=get-cluster
if (!($failoverClusterModuleAvailable)){
if(!(checkProxy)){"Internet issues detected. Attempting a fix now..."; fixProxy;}
installPrerequisites;
}

function failoverClusterDiscovery{
queryFailoverClusters;

$pickedItemWinRM=Test-NetConnection -ComputerName "$pickedItem`.$($env:USERDNSDOMAIN)" -CommonTCPPort WINRM -InformationLevel Quiet
if($pickedItemWinRM){
"Obtaining Cluster Resource from node $pickedItem..."

# Triggering invoke-command as multi-threaded jobs so that the program will wait until these jobs are done before moving to the Pause command
$jobs = Invoke-Command -ComputerName $pickedItem -ScriptBlock {Get-ClusterResource | format-list Name,OwnerNode;} -AsJob
While ($Jobs.State -Contains "Running"){
$Jobs | Wait-Job -Any;
if ($jobs.State -contains "Completed") {
Receive-Job -Job $jobs;
Remove-Job -Job $jobs;
}
}

#Invoke-Command -ComputerName $pickedItem -ScriptBlock{Get-ClusterResource;}
}else{
"$pickedItem is not reachable via Remote PowerShell to query Cluster Resources.";
pause;
}
}

do {
failoverClusterDiscovery;
[string]$response=Read-Host -Prompt "`nPress any key to repeat process. Type 'exit' or use Ctrl+C to terminate program"
} until ($response -like "exit")

Leave a Reply

Your email address will not be published. Required fields are marked *