Posted On August 22, 2020

PowerShell: List All Hyper-V Snapshots of All VMs in All Clusters in Domain

kimconnect 0 comments
blog.KimConnect.com >> Codes , Virtualization >> PowerShell: List All Hyper-V Snapshots of All VMs in All Clusters in Domain
# listHyperVSnapshots.ps1

$clusterName='*'

function listAllHyperVSnapshots([string[]]$clusterName){
    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 getAllVms([string[]]$clusterName,$verbose=$true){
    
        if(!$clusterName -or $clusterName -eq '*'){
            $result=@()
            $domains=(Get-ADForest).Name|%{(Get-ADForest -Identity $_).Name}        
            foreach ($domain in $domains){
                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|?{$_.GroupType -eq 'VirtualMachine'}}catch{}}
                if($verbose){
                    $elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
                    write-host "Minutes elapsed $elapsed`: Node and VM names collected for $domain"
                    }
                $result+=$nodes
                }
            return $result
        }else{
            $nodes=$clusterName|%{Get-ClusterGroup -Cluster $_ -ea SilentlyContinue}|?{$_.GroupType -eq 'VirtualMachine'}
            if($verbose){
                $elapsed=[math]::round($timer.Elapsed.TotalMinutes,2)
                write-host "Minutes elapsed $elapsed`: Node and VM names collected for $domain"
                }
            return $nodes
            }
    
    }

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

    $checkpoints=@()
    try{
        includeRSAT;
        $allVms=getAllVms $clusterName
    }catch{
        Write-error $_
        return $false
        }
    foreach ($vm in $allVms){
        $vmName=$vm.Name
        $ownerNode=$vm.OwnerNode.Name
        $vmState=$vm.State
        if($vm.OwnerNode.State -eq 'Up'){
            try{ 
                $result=Get-VMSnapshot -VMName $vmName -ComputerName $ownerNode
                $checkPoint=if($result){$result}else{'None'}
                $isValidName=$true
            }catch{
                $checkPoint='Unknown'
                $isValidName=$false
                }
            write-host "$vmName`t: $(($checkpoint|out-string).trim())"
            $checkpoints+=[pscustomobject]@{ownerNode=$ownerNode;vmName=$vmName;vmState=$vmState;checkpoints=$checkpoint;validName=$isValidName}
        }else{z
            $checkpoints+=[pscustomobject]@{ownerNode=$ownerNode;vmName=$vmName;vmState='Unknown';checkpoints='Unknown';validName='Unknown'}
            }
        }
    return $checkPoints
}

listAllHyperVSnapshots

Leave a Reply

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

Related Post

VMware Virtual Disk Manager Does Not Expand Partitions

If you are using the VMware Virtual Disk Manager included in GSX Server, VMware Server,…

How To Copy Folder in Legacy Windows with Long File Names?

$sourceDirectory='D:\SMBSHARE\LONGPATH' $destinationDirectory='\\FILESERVER\LONGPATH' function copyFolderWithLongNames($sourceDirectory,$destinationDirectory){ $sourceParent=split-path $sourceDirectory -parent $sourceChild=split-path $sourceDirectory -leaf $destinationParent=split-path $destinationDirectory -parent $destinationChild=split-path $destinationDirectory…

Indications that Chocolatey is locked down

# System's version is less than vendor's current (Chocolatey v0.10.15) 0+000+00[LAX-WEB005]: PS C:\Users\testadmin\Documents> choco source…