PowerShell: Get Windows Resource Utilization

# getWindowsResourceUtilization.ps1
# version 0.02
# Gather information on a list of Windows Machines
# This version will test RCP or WinRM access from localhost toward each destination
# If RPC is unavailable, then WinRM would be tried
# If neither procotol is reachable from localhost toward destination(s), then such machine scanning will be skipped

$computernames=@(
    'LAX-RDSNODE01',
    'LAX-RDSNODE02',
    'LAX-RDSNODE03',
    'LAX-RDSNODE04'
)

function checkWindows($computernames=$env:computername){

    function checkPort($computername=$env:computername,$port=135,$protocol='tcp',$verbose=$false){
        function includePortQry{
            if (!(Get-Command portqry.exe -ErrorAction SilentlyContinue)){
                if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))}
                choco install portqry -y
                if (Get-Command portqry.exe -ErrorAction SilentlyContinue){return $true}else{return $false}
            }else{
                return $true
            }
        }
        
        $portQryExists=includePortQry
        if($portQryExists){        
            $ip=[System.Net.Dns]::GetHostAddresses($computername).IPAddressToString|select-object -first 1
            $reachable=if($port -ne 135){[bool](portqry -n $ip -p $protocol -e $port|find ': LISTENING')
                }else{
                    [bool](portqry -n $ip -p $protocol -e $port|find 'Total endpoints found: ')
                }       
            if($verbose){write-host "$port/$protocol : $reachable"}
            $result=[PSCustomObject]@{
                source=$env:computername
                destination=$computername
                port=$port
                protocol=$protocol
                reachable=$reachable
            }
        }else{
            write-warning 'Unable to proceed without PortQry'
            return $null
        }
        return $result
    }
    function checkComputer($computername=$env:computername){
        $excludeServiceNames='conhost|dasHost|dllhost|ctfmon|audiodg|cytray|ApplicationFrameHost|SYSTEM|LOCAL SERVICE|NETWORK SERVICE|DWM-\d+|UMFD-\d+'
        $cpuObject = Get-WmiObject -computername $computername win32_processor -ea stop |select Name,NumberOfLogicalProcessors,LoadPercentage
        $cpuCount=($cpuObject|  Measure-Object -property NumberOfLogicalProcessors -Sum).Sum
        $cpuLoad = ($cpuObject|  Measure-Object -property LoadPercentage -Average | Select @{Name="CurrentLoad";Expression={"{0:N2} %" -f ($_.Average)}}).CurrentLoad
        $osAndMemory = gwmi -Class win32_operatingsystem -computername $computername -ea stop| select @{Name="os";Expression={$_.Caption}},@{Name="Memory";Expression={"{0:N2} GB" -f ($_.TotalVisibleMemorySize / 1048576)}},@{Name = "Utilization"; Expression = {"{0:N2} %" -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize) }}
        if($osAndMemory){
            $os=$osAndMemory.os;
            $memory=$osAndMemory.Memory;
            $memoryUtilization=$osAndMemory.Utilization;
            }
        $volumes=gwmi -Class win32_volume -computername $computername -Filter "DriveType!=5" -ea stop| `
                    ?{$_.DriveLetter -ne $isnull}|  `
                        Select-object @{Name="Letter";Expression={$_.DriveLetter}}, `
                            @{Name="Label";Expression={$_.Label}},@{Name="Capacity";Expression={"{0:N2} GiB" -f ($_.Capacity/1073741824)}}, `
                            @{Name = "Utilization"; Expression = {"{0:N2} %" -f  ((($_.Capacity-$_.FreeSpace) / $_.Capacity)*100) }}| `
                        Sort-Object -Property Letter
        $topProcesses=get-process -computername $computername|Group-Object -Property ProcessName | `
                        Select-Object -First 10 | `
                        Select-Object Name, @{N='Memory (MB)';E={[math]::Round((($_.Group|Measure-Object WorkingSet -Sum).Sum/1MB),2)}} | `
                        Sort-Object -Property "Memory (MB)" -Descending| `
                        ?{$_.Name -ne '' -and $_.Name -notmatch $excludeServiceNames}
        $memoryUsagePerUser=get-wmiobject win32_process -computername $computername| `
                            select-object @{N='User';E={$_.getowner().user}}, WorkingSetSize  | Group-Object user | `
                            select-object Name, @{N='Memory (MB)';E={[math]::Round(($_.Group.WorkingSetSize  | Measure-Object -Sum).Sum / 1Mb,2)  }} | `
                            ?{$_.Name -ne '' -and $_.Name -notmatch $excludeServiceNames} | sort-object -property "Memory (MB)" -Descending
        
        function getSessionsInfo([string[]]$computername=$env:computername){
            $results=@()
            function getDisconnectedSessionInfo($thisLine,$computer){
                # convert multiple spaces into single space and split pieces into array
                $sessionArray = $thisLine.Trim() -Replace '\s+',' ' -Split '\s'
                $properties = @{
                    UserName = $sessionArray[0]
                    ComputerName = $computer
                    }
                $properties.SessionName = $null
                $properties.SessionId = $sessionArray[1]
                $properties.State = $sessionArray[2]                        
                $properties.IdleMinutes=.{
                        [string]$x=$sessionArray[3]
                        switch -regex ($x){
                            '\.' {0;break}                   
                            '\+' {$dayMinutes=.{[void]($x -match '^(\d+)\+');[int]($matches[1])*1440}
                                    $hourMinutes=.{[void]($x -match '\+(.*)$');([TimeSpan]::Parse($matches[1])).totalMinutes}
                                    $dayMinutes+$hourMinutes
                                    break;
                                    }               
                            '\:' {try{
                                    ([TimeSpan]::Parse($x)).totalMinutes
                                    }catch{
                                        "Invalid value: $x"
                                        }
                                    break
                                    }
                            default {$x}
                        }                                  
                    }
                $properties.LogonTime = $sessionArray[4..6] -join ' '
                $result=New-Object -TypeName PSCustomObject -Property $properties
                return $result
                }
                    
            function getActiveSessionInfo($thisLine,$computer){
                $sessionArray = $thisLine.Trim() -Replace '\s+',' ' -Split '\s'
                $properties = @{
                    UserName = $sessionArray[0]
                    ComputerName = $computer
                    }
                $properties.SessionName = $sessionArray[1]
                $properties.SessionId = $sessionArray[2]
                $properties.State = $sessionArray[3]
                $properties.IdleMinutes=.{
                        $x=$sessionArray[4]
                        switch -regex ($x){
                            '\.' {0;break}                   
                            '\+' {  $dayMinutes=.{[void]($x -match '^(\d+)\+');[int]($matches[1])*1440}
                                    $hourMinutes=.{[void]($x -match '\+(.*)$');([TimeSpan]::Parse($matches[1])).totalMinutes}
                                    $dayMinutes+$hourMinutes
                                    break;
                                    }               
                            '\:' {  $timeFragments=[regex]::match($x,'(\d+)\:(\d+)')
                                    [Int16]$hours=$timeFragments.Groups[1].Value
                                    [Int16]$minutes=$timeFragments.Groups[2].Value
                                    $minutes + ($hours * 60)
                                    break
                                    }
                            default {$x}
                        }
                    }
                $properties.LogonTime = $sessionArray[5..($sessionArray.GetUpperBound(0))] -join ' '
                $result=New-Object -TypeName PSCustomObject -Property $properties
                return $result
                }
        
            foreach ($computer in $computername){
                try {
                    # Perusing legacy commandlets as there are no PowerShell equivalents at this time
                    $sessions=quser /server:$computer 2>&1 | Select-Object -Skip 1
                    ForEach ($line in $sessions) {               
                        $fragments = $line.Trim() -Replace '\s+',' ' -Split '\s'
                        $disconnectedSession=$fragments[2] -eq 'Disc' -or $fragments[3] -eq 'Disc'
                        if ($disconnectedSession){
                            $result=getDisconnectedSessionInfo $line $computer
                        }else{
                            $result=getActiveSessionInfo $line $computer
                        }
                        if($result){$results+=$result}
                    }
                }catch{
                    $results+=New-Object -TypeName PSCustomObject -Property @{
                        ComputerName=$computer
                        Error=$_.Exception.Message
                    } | Select-Object -Property UserName,ComputerName,SessionName,SessionId,State,IdleMinutes,LogonTime,Error|sort -Property UserName
                }
            }
            return $results
        }
        $activeUsers=(getSessionsInfo $computername|?{$_.State -eq 'Active'}).UserName
        return [PSCustomObject]@{
            computername=$computername
            os= $os
            cpuCount= $cpuCount
            cpuLoad= $cpuLoad
            ramCapacity= $memory
            ramUtilization= $memoryUtilization
            volumes=($volumes|out-string).trim()
            topProcesses=($topProcesses|out-string).trim()
            memoryUsagePerUser=($memoryUsagePerUser|out-string).trim()
            activeUsers=($activeUsers|out-string).trim()
        }
    }
    $results=@()
    foreach ($computer in $computernames){
        try{
            write-host "checking $computer..."
            $rpcPortOpen=(checkport $computer 135).reachable            
            if($rpcPortOpen){
                $result=checkComputer $computer
                $results+=$result
            }elseif((checkport $computer 5985).reachable){
                $session=new-pssession $computer
                if($session.State -eq 'Opened'){
                    $result=invoke-command -session $session -scriptblock{
                        param($checkcomputer)
                        [scriptblock]::create($checkcomputer).invoke()
                    } -Args ${function:checkComputer}|select-object * -ExcludeProperty pscomputername,runspaceid,PSShowComputerName
                    $results+=$result
                    remove-pssession $session
                }else{
                    write-warning "$env:computername cannot initiate a WinRM session toward $computer"
                }                
            }else{
                write-warning "$env:computername is unable to reach $computer"
            }
        }catch{
            write-host $_            
        }
    }
    return $results
}

$systemUtilization=checkWindows $computernames
$systemUtilization|select-object computername,cpuCount,cpuLoad,ramCapacity,ramUtilization,activeUsers|ft -Wrap 
# getWindowsResourceUtilization.ps1
# Gather information on a list of Windows Machines
# Requires WMI Ports Access:
# 135/TCP
# 49152-65535/TCP (RPC dynamic ports – Windows Vista, 2008 and above)
# 1024-65535/TCP (RPC dynamic ports – Windows NT4, Windows 2000, Windows 2003)
# 445/TCP - Windows Performance Counter Access (SMB, RPC/NP)

$computernames=@(
    'LAX-RDSNODE01',
    'LAX-RDSNODE02',
    'LAX-RDSNODE03',
    'LAX-RDSNODE04'
)

function checkWindows($computernames=$env:computername){
    function checkComputer($computername=$env:computername){
        $excludeServiceNames='conhost|dasHost|dllhost|ctfmon|audiodg|cytray|ApplicationFrameHost|SYSTEM|LOCAL SERVICE|NETWORK SERVICE|DWM-\d+|UMFD-\d+'
        $cpuObject = Get-WmiObject -computername $computername win32_processor -ea stop |select Name,NumberOfLogicalProcessors,LoadPercentage
        $cpuCount=($cpuObject|  Measure-Object -property NumberOfLogicalProcessors -Sum).Sum
        $cpuLoad = ($cpuObject|  Measure-Object -property LoadPercentage -Average | Select @{Name="CurrentLoad";Expression={"{0:N2} %" -f ($_.Average)}}).CurrentLoad
        $osAndMemory = gwmi -Class win32_operatingsystem -computername $computername -ea stop| select @{Name="os";Expression={$_.Caption}},@{Name="Memory";Expression={"{0:N2} GB" -f ($_.TotalVisibleMemorySize / 1048576)}},@{Name = "Utilization"; Expression = {"{0:N2} %" -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize) }}
        if($osAndMemory){
            $os=$osAndMemory.os;
            $memory=$osAndMemory.Memory;
            $memoryUtilization=$osAndMemory.Utilization;
            }
        $volumes=gwmi -Class win32_volume -computername $computername -Filter "DriveType!=5" -ea stop| `
                    ?{$_.DriveLetter -ne $isnull}|  `
                        Select-object @{Name="Letter";Expression={$_.DriveLetter}}, `
                            @{Name="Label";Expression={$_.Label}},@{Name="Capacity";Expression={"{0:N2} GiB" -f ($_.Capacity/1073741824)}}, `
                            @{Name = "Utilization"; Expression = {"{0:N2} %" -f  ((($_.Capacity-$_.FreeSpace) / $_.Capacity)*100) }}| `
                        Sort-Object -Property Letter
        $topProcesses=get-process -computername $computername|Group-Object -Property ProcessName | `
                        Select-Object -First 10 | `
                        Select-Object Name, @{N='Memory (MB)';E={[math]::Round((($_.Group|Measure-Object WorkingSet -Sum).Sum/1MB),2)}} | `
                        Sort-Object -Property "Memory (MB)" -Descending| `
                        ?{$_.Name -ne '' -and $_.Name -notmatch $excludeServiceNames}
        $memoryUsagePerUser=get-wmiobject win32_process -computername $computername| `
                            select-object @{N='User';E={$_.getowner().user}}, WorkingSetSize  | Group-Object user | `
                            select-object Name, @{N='Memory (MB)';E={[math]::Round(($_.Group.WorkingSetSize  | Measure-Object -Sum).Sum / 1Mb,2)  }} | `
                            ?{$_.Name -ne '' -and $_.Name -notmatch $excludeServiceNames} | sort-object -property "Memory (MB)" -Descending
        
        function getSessionsInfo([string[]]$computername=$env:computername){
            $results=@()
            function getDisconnectedSessionInfo($thisLine,$computer){
                # convert multiple spaces into single space and split pieces into array
                $sessionArray = $thisLine.Trim() -Replace '\s+',' ' -Split '\s'
                $properties = @{
                    UserName = $sessionArray[0]
                    ComputerName = $computer
                    }
                $properties.SessionName = $null
                $properties.SessionId = $sessionArray[1]
                $properties.State = $sessionArray[2]                        
                $properties.IdleMinutes=.{
                        [string]$x=$sessionArray[3]
                        switch -regex ($x){
                            '\.' {0;break}                   
                            '\+' {$dayMinutes=.{[void]($x -match '^(\d+)\+');[int]($matches[1])*1440}
                                    $hourMinutes=.{[void]($x -match '\+(.*)$');([TimeSpan]::Parse($matches[1])).totalMinutes}
                                    $dayMinutes+$hourMinutes
                                    break;
                                    }               
                            '\:' {try{
                                    ([TimeSpan]::Parse($x)).totalMinutes
                                    }catch{
                                        "Invalid value: $x"
                                        }
                                    break
                                    }
                            default {$x}
                        }                                  
                    }
                $properties.LogonTime = $sessionArray[4..6] -join ' '
                $result=New-Object -TypeName PSCustomObject -Property $properties
                return $result
                }
                    
            function getActiveSessionInfo($thisLine,$computer){
                $sessionArray = $thisLine.Trim() -Replace '\s+',' ' -Split '\s'
                $properties = @{
                    UserName = $sessionArray[0]
                    ComputerName = $computer
                    }
                $properties.SessionName = $sessionArray[1]
                $properties.SessionId = $sessionArray[2]
                $properties.State = $sessionArray[3]
                $properties.IdleMinutes=.{
                        $x=$sessionArray[4]
                        switch -regex ($x){
                            '\.' {0;break}                   
                            '\+' {  $dayMinutes=.{[void]($x -match '^(\d+)\+');[int]($matches[1])*1440}
                                    $hourMinutes=.{[void]($x -match '\+(.*)$');([TimeSpan]::Parse($matches[1])).totalMinutes}
                                    $dayMinutes+$hourMinutes
                                    break;
                                    }               
                            '\:' {  $timeFragments=[regex]::match($x,'(\d+)\:(\d+)')
                                    [Int16]$hours=$timeFragments.Groups[1].Value
                                    [Int16]$minutes=$timeFragments.Groups[2].Value
                                    $minutes + ($hours * 60)
                                    break
                                    }
                            default {$x}
                        }
                    }
                $properties.LogonTime = $sessionArray[5..($sessionArray.GetUpperBound(0))] -join ' '
                $result=New-Object -TypeName PSCustomObject -Property $properties
                return $result
                }
        
            foreach ($computer in $computername){
                try {
                    # Perusing legacy commandlets as there are no PowerShell equivalents at this time
                    $sessions=quser /server:$computer 2>&1 | Select-Object -Skip 1
                    ForEach ($line in $sessions) {               
                        $fragments = $line.Trim() -Replace '\s+',' ' -Split '\s'
                        $disconnectedSession=$fragments[2] -eq 'Disc' -or $fragments[3] -eq 'Disc'
                        if ($disconnectedSession){
                            $result=getDisconnectedSessionInfo $line $computer
                        }else{
                            $result=getActiveSessionInfo $line $computer
                        }
                        if($result){$results+=$result}
                    }
                }catch{
                    $results+=New-Object -TypeName PSCustomObject -Property @{
                        ComputerName=$computer
                        Error=$_.Exception.Message
                    } | Select-Object -Property UserName,ComputerName,SessionName,SessionId,State,IdleMinutes,LogonTime,Error|sort -Property UserName
                }
            }
            return $results
        }
        $activeUsers=(getSessionsInfo $computername|?{$_.State -eq 'Active'}).UserName
        return [PSCustomObject]@{
            computername=$computername
            os= $os
            cpuCount= $cpuCount
            cpuLoad= $cpuLoad
            ramCapacity= $memory
            ramUtilization= $memoryUtilization
            volumes=($volumes|out-string).trim()
            topProcesses=($topProcesses|out-string).trim()
            memoryUsagePerUser=($memoryUsagePerUser|out-string).trim()
            activeUsers=$activeUsers
        }
    }
    $results=@()
    foreach ($computer in $computernames){
        try{
            write-host "checking $computer..."
            $result=checkComputer $computer
            $results+=$result
        }catch{
            write-host $_            
        }
    }
    return $results
}

$systemUtilization=checkWindows $computernames
$systemUtilization|select-object computername,cpuCount,cpuLoad,ramCapacity,ramUtilization,activeUsers|ft -Wrap 
# computername  cpuCount cpuLoad ramCapacity ramUtilization activeUsers
# ------------  -------- ------- ----------- -------------- -----------
# LAX-RDSNODE01        8 1.00 %  64.00 GB    8.46 %         brucelee
# LAX-RDSNODE02        8 0.00 %  64.00 GB    21.90 %        leeSandwich
#                                                           mcDonalds
# LAX-RDSNODE03        8 48.00 % 64.00 GB    25.84 %        rambo
#                                                           mickeyMouse
# LAX-RDSNODE04        8 1.00 %  64.00 GB    21.90 %        oneMillionDollars
#                                                           20Cents

Leave a Reply

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