# 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){
function checkPendingReboot([string]$computer=$ENV:computername){
function checkRegistry{
if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore) { return $true }
if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootInProgress" -EA Ignore) { return $true }
if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore) { return $true }
if (Get-Item "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending" -EA Ignore) { return $true }
if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\PostRebootReporting" -EA Ignore) { return $true }
if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -EA Ignore) { return $true }
if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations2 -EA Ignore) { return $true }
if (Get-Item "HKLM:\SOFTWARE\Microsoft\ServerManager\CurrentRebootAttempts" -EA Ignore) { return $true }
try{
$ccmUtil=[wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
$status=$ccmUtil.DetermineIfRebootPending()
if(($null -ne $status) -and $status.RebootPending){
#$result.SCCMRebootPending = $true
return $true
}
}catch{}
}
function checkRegistryRemotely{ # need to QA this snippet
param(
$remoteComputer,
$registryHive=[Microsoft.Win32.RegistryHive]::LocalMachine,
$registryPath,
$keyName
)
$remoteComputer='???'
$registryHive=[Microsoft.Win32.RegistryHive]::LocalMachine
$registryPath='Software\Microsoft\Windows\CurrentVersion\Component Based Servicing'
$keyName='Version'
$regHive=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($registryHive,$remoteComputer)
$key=$regHive.OpenSubKey($registryPath)
$regHive.GetValue($keyName)
}
if ($ENV:computername -eq $computer){
$result=checkRegistry;
}else{
$result=Invoke-Command -computername $computer -ScriptBlock{
param($importedFunc);
[ScriptBlock]::Create($importedFunc).Invoke();
} -ArgumentList ${function:checkRegistry}
}
return $result;
}
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
}
$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 -EA Ignore|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}
$sessions=getSessionsInfo $computername
$memoryUsagePerUser=get-wmiobject win32_process -computername $computername| `
select-object @{N='User';E={$_.getowner().user}}, WorkingSetSize | Group-Object user | `
where-object {$_.Name -ne '' -and $_.Name -notmatch $excludeServiceNames} | `
select-object Name,
@{N='Memory (MB)';E={[math]::Round(($_.Group.WorkingSetSize | Measure-Object -Sum).Sum / 1Mb,2) }},
@{N='Status';E={$x=$_.Name;$($sessions|?{$_.UserName -eq $x}).State }},
@{N='IdleMinutes';E={$x=$_.Name;$($sessions|?{$_.UserName -eq $x}).IdleMinutes }} | `
sort-object -property "Memory (MB)" -Descending
# $activeUsers=(getSessionsInfo $computername|?{$_.State -eq 'Active'}).UserName
$lastUpdates=get-hotfix|select HotfixID,Description,InstalledOn|?{$_.installedon -gt (get-date).addDays(-30)}
$appWiz=try{
Get-Package -Provider Programs -IncludeWindowsInstaller|select-object Name,Version|sort -property Name
}catch{
Get-WmiObject -Class Win32_Product|select-object Name,Version|sort -property Name
}
$lastbootTime=(Get-CimInstance -ClassName win32_operatingsystem -computername $computername).lastbootuptime.toString().trim()
#Preempt this error:
#The term 'Get-Package' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
#spelling of the name, or if a path was included, verify that the path is correct and try again.
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()
installedApps=($appWiz|out-string).trim()
lastUpdates=($lastUpdates|out-string).trim()
pendingReboot=if(checkPendingReboot){$true}else{$false}
lastBootupTime=$lastbootTime
}
}
$results=@()
foreach ($computer in $computernames){
try{
write-host "checking $computer..."
#$rpcPortOpen=if($env:computername -in @($computer,"$computer.$env:userdnsdomain")){$true}else{(checkport $computer 135).reachable}
$rpcPortOpen=if($env:computername -in @($computer,"$computer.$env:userdnsdomain")){
$true
}else{
test-netconnection $computer -port 135 -informationlevel quiet
}
if($rpcPortOpen){
$result=checkComputer $computer
$results+=$result
#}elseif((checkport $computer 5985).reachable){
}elseif(test-netconnection $computer -port 5985 -informationlevel quiet){
$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 not able to reach $computer via RPC nor WinRM"
}
}catch{
write-host $_
}
}
return $results
}
# enter-pssession DYNAMICS-CRM -SessionOption $(new-pssessionoption -IncludePortInSPN)
$systemUtilization=checkWindows $computernames
$systemUtilization |select-object computername,cpuCount,cpuLoad,ramCapacity,ramUtilization,activeUsers,volumes|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
Categories: