# mtuReportAllHyperVHosts.ps1

# Ensure that AD management module is available for PS Session
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 "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 "Module ActiveDirectory IS currently available on this system." -ForegroundColor Green
        return $true
        }
}

function ListHyperVHosts {            
      [cmdletbinding()]            
      param(
        [string]$forest
      )            
      try {            
       Import-Module ActiveDirectory -ErrorAction Stop            
      } catch {            
       Write-Warning "Failed to import Active Directory module. Exiting program..."           
       break;
      }            
 
      $domains=(Get-ADForest -Identity $forest).Domains 
      foreach ($domain in $domains){
      [string]$dc=(get-addomaincontroller -DomainName $domain -Discover -NextClosestSite).HostName
      try {             
       $hyperVs = Get-ADObject -Server $dc -Filter 'ObjectClass -eq "serviceConnectionPoint" -and Name -eq "Microsoft Hyper-V"' -ErrorAction Stop;
      }catch{            
       write-warning "Failed to query $dc of $domain";         
        }            
      foreach($hyperV in $hyperVs) {            
         $x = $hyperV.DistinguishedName.split(",")            
         $HypervDN = $x[1..$x.Count] -join "," 
       
         if ( !($HypervDN -match "CN=LostAndFound")) {     
         $computer = Get-ADComputer -Id $HypervDN -Prop *
         $thisObject = New-Object PSObject -Prop (
             @{
                 hostname = $computer.Name
                 operatingSystem = $($computer.operatingSystem)
                 })
             $thisObject
             }           
        }
       }
    }

function listForests{
    $GLOBAL:forests=Get-ADForest | select Name;
    if ($forests.length -gt 1){
        return $forests | %{$_.Name;}
    }else{
        return $forests.Name;
    }
}

function mtuReport($computernames){
    write-host "Please wait while we scan $($computernames.count) nodes."
    $report=$computernames|%{
        write-host "Scanning $_..."
        try{
            $result=invoke-command -computername $_ -ScriptBlock{
                $adapters=Get-NetAdapter
                return $adapters|select name,mtusize
                }|select @{name='computerName';e={$_.PSComputerName}},@{name='adapterName';e={$_.Name}},MtuSize
        }catch{
            write-warning $error[0].Exception.Message
            continue;
        }finally{
            if($result){$result}
            }
    }
    $highLights=@{
        $true='White'
        $false='Yellow'
        }
    $mtuReport=$report|select-object computerName,adapterName,MtuSize,@{Name='highlight';e={$highLights[$_.MtuSize -eq 1500]}}
    $mtuReport|%{write-host $($_|select-object -Property * -ExcludeProperty highlight) -ForegroundColor $_.highlight}
    return $report
}

function sortArrayStringAsNumbers([string[]]$names){
    $hashTable=@{}
    foreach ($name in $names){
        [int]$x=.{[void]($name -match '(?:.(\d+))+$');$matches[1]}
        $hashTable.Add($name,$x)
        }
    $sorted=foreach($item in $hashTable.GetEnumerator() | Sort Value,Name){$item.Name}
    return $sorted
}

includeRSAT;
$hyperVHosts=listForests|%{ListHyperVHosts $_}
$hyperVHostNames=sortArrayStringAsNumbers $hyperVHosts.hostname
$mtuReport=mtuReport $hyperVHostNames
# Bonus round: get all clusters in the domain - this takes a long time for a large network
$clustersInDomain=Get-Cluster -domain $env:USERDNSDOMAIN|Get-ClusterGroup
# Filter down to VirtualMachine groups
$clustersInDomain|?{$_.GroupType -eq 'VirtualMachine'}
$vmClustersInDomain=$clustersInDomain|Group-Object Cluster|Select-Object Name
$vmClustersInDomain