This is a working version to correct my previous codes posted somewhere on this site – I know, I should use Tortoise or gitlab to keep track of these things. Anywho, the previous iteration has returned results faster at the cost of being inconclusive of all available nodes residing in the domain. Although I dislike the inefficiency of this current method, it seems to be the only feasible route to achieve accurate results.
function getHyperVHostsInForest{
function includeRSAT{
$ErrorActionPreference='stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#$rsatWindows7x32='https://download.microsoft.com/download/4/F/7/4F71806A-1C56-4EF2-9B4F-9870C4CFD2EE/Windows6.1-KB958830-x86-RefreshPkg.msu'
$rsatWindows7x64='https://download.microsoft.com/download/4/F/7/4F71806A-1C56-4EF2-9B4F-9870C4CFD2EE/Windows6.1-KB958830-x64-RefreshPkg.msu'
$rsatWindows81='https://download.microsoft.com/download/1/8/E/18EA4843-C596-4542-9236-DE46F780806E/Windows8.1-KB2693643-x64.msu'
$rsat1709 = "https://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-RSAT_WS_1709-x64.msu"
$rsat1803 = "https://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-RSAT_WS_1803-x64.msu"
$rsatWs2016 = "https://download.microsoft.com/download/1/D/8/1D8B5022-5477-4B9A-8104-6A71FF9D98AB/WindowsTH-RSAT_WS2016-x64.msu"
# This command does not work on Windows 2012R2
#$releaseId=(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId).ReleaseId
#Get-ItemProperty : Property ReleaseId does not exist at path HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows
#NT\CurrentVersion.
#At line:1 char:2
#+ (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Na ...
#+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# + CategoryInfo : InvalidArgument: (ReleaseId:String) [Get-ItemProperty], PSArgumentException
# + FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
$releaseId=(Get-Item "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue('ReleaseID')
$osVersion=[System.Environment]::OSVersion.Version
[double]$osVersionMajorMinor="$($osVersion.Major).$($osVersion.Minor)"
$osName=(Get-WmiObject Win32_OperatingSystem).Name
#$osType=switch ((Get-CimInstance -ClassName Win32_OperatingSystem).ProductType){
# 1 {'client'}
# 2 {'domaincontroller'}
# 3 {'memberserver'}
# }
$windowsVersion=(Get-CimInstance Win32_OperatingSystem).Version
switch ($releaseId){
1607{write-host 'Windows Server 2016 Release 1607 detected';$link=$rsatWs2016;break}
1709{write-host 'Windows Server 2016 Release 1709 detected';$link=$rsat1709;break}
1803{write-host 'Windows Server 2016 Release 1803 detected';$link=$rsat1803}
}
switch ($osVersionMajorMinor){
{$_ -eq 6.0}{write-host 'Windows Server 2008 or Windows Vista detected';$link=$rsat1709;break}
{$_ -eq 6.1}{write-host 'Windows Server 2008 R2 or Windows 7 detected';$link=$rsatWindows7x64;break}
{$_ -eq 6.2}{write-host 'Windows Server 2012 or Windows 8.1 detected';$link=$rsatWindows81;break}
{$_ -eq 6.3}{write-host 'Windows Server 2012 R2 detected';$link=$rsatWindows81}
}
if (!(Get-Module -ListAvailable -Name ActiveDirectory -EA SilentlyContinue)){
Write-host "Prerequisite checks: module ActiveDirectory NOT currently available on this system. Please wait while the program adds that plugin..."
try{
# If OS is Windows Server, then install RSAT using a different method
if ($osName -match "^Microsoft Windows Server") {
# This sequence has confirmed to be valid on Windows Server 2008 R2 and above
Write-Verbose "Importing Windows Feature: RSAT-AD-PowerShell"
Import-Module ServerManager
Add-WindowsFeature RSAT-AD-PowerShell
}
else{
Write-Verbose "This sequence targets Windows Client versions"
$destinationFile= ($ENV:USERPROFILE) + "\Downloads\" + (split-path $link -leaf)
Write-Host "Downloading RSAT from $link..."
Start-BitsTransfer -Source $link -Destination $destinationFile
$fileCheck=Get-AuthenticodeSignature $destinationFile
if($fileCheck.status -ne "valid") {write-host "$destinationFile is not valid. Please try again...";break}
$wusaCommand = $destinationFile + " /quiet"
Write-host "Installing RSAT - please wait..."
Start-Process -FilePath "C:\Windows\System32\wusa.exe" -ArgumentList $wusaCommand -Wait
}
return $true
}
catch{
write-warning "$($error[0].Exception)"
return $false
}
}else{
Write-host "Prerequisite checks: module ActiveDirectory IS currently available on this system." -ForegroundColor Green
return $true
}
}
function listAllHyperVNodes($verbose=$true){
try{
$timer=[System.Diagnostics.Stopwatch]::StartNew()
$domains=(Get-ADForest).Name|%{(Get-ADForest -Identity $_).Name}
foreach ($domain in $domains){
#[string]$dc=(get-addomaincontroller -DomainName "$domain" -Discover -NextClosestSite).HostName
write-host "Collecting all Hyper-V Clusters in $domain. This may take a while, depending on cluster sizes."
$allClusters=(get-cluster -domain $domain).Name
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: cluster names collected"
}
$nodes=$allClusters|%{try{Get-ClusterGroup -Cluster $_ -ea SilentlyContinue}catch{}}
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: Hyper V node names collected"
}
$hyperVs=($nodes|Group-Object -Property OwnerNode).Name
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: Hyper V node names collected - Done!"
}
}
return $hyperVs
}catch{
Write-Error $_
return $false
}
}
function sortArrayStringAsNumbers([string[]]$names){
$hashTable=@{}
foreach ($name in $names){
$value=.{
[int]([regex]::match($name,'^(.*?)(\d+)(\D*)$').groups[2].value)
}
$hashTable.Add($name,$value)
}
$sorted=foreach($item in $hashTable.GetEnumerator() | Sort Value){$item.Name}
return $sorted
}
try{
$null=includeRSAT;
$hyperVHosts=listAllHyperVNodes
$hyperVHostNames=sortArrayStringAsNumbers $hyperVHosts
return $hyperVHostNames
}catch{
Write-Error $_
return $false
}
}
# Short Version
function listAllHyperVNodes($verbose=$true){
try{
$timer=[System.Diagnostics.Stopwatch]::StartNew()
$domains=(Get-ADForest).Name|%{(Get-ADForest -Identity $_).Name}
foreach ($domain in $domains){
#[string]$dc=(get-addomaincontroller -DomainName "$domain" -Discover -NextClosestSite).HostName
write-host "Collecting all Hyper-V Clusters in $domain. This may take a while, depending on cluster sizes."
$allClusters=(get-cluster -domain $domain).Name
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: cluster names collected"
}
$nodes=$allClusters|%{try{Get-ClusterGroup -Cluster $_ -ea SilentlyContinue}catch{}}
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: Hyper V node names collected"
}
$hyperVs=($nodes|Group-Object -Property OwnerNode).Name
if($verbose){
$elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
write-host "Minutes elapsed $elapsed`: Hyper V node names collected - Done!"
}
}
return $hyperVs
}catch{
Write-Error $_
return $false
}
}
Categories: