Archive | December 2015

Get current cpu usage for a process

I wanted to get the current CPU usage for certain processes, which appeared to be harder than I thought.

The Get-Process command will give you CPU time and program start time, but will not tell you when the CPU was being used by the process. Thus if you use the previous information you got from the tool to calculate the CPU usage, you’ll get the average CPU usage for that process since it started. (So if it started at 100% CPU usage and has been busy since, it’ll show as 100%, but when it’s software that’s been started days ago and just been using 100% for the past 10 minutes, without doing much the time before, it’ll show as the program using 0,05 % or something like that, since that is its average usage). If you’re interested in this information, Powershell.com explains how to do this. This is not the information I want to know, thus I started searching for another way to do it.

WMI has many possibilities, but most of my scripts that are lagging, are lagging because of WMI usage, thus I try to use this only if I can’t find another way to do so.

I finally got to the Get-Counter command, which gets information from Performance Counters. I’d like to get % processor time information, thus I’ll check this with the following command (add the * to get all instances of the process):

Get-Counter "\Process(Chrome*)\% Processor Time"

This gives me a list of all process instances and their current CPU usage.

If you’d want to take multiple samples, this can also be specified (just like the sample interval)

Get-Counter "\Process(Chrome*)\% Processor Time" -SampleInterval 1 -MaxSamples 5

At the moment I’m only interested in getting 1 quick sample (this script will be integrated in a monitoring tool), thus I’ll skip these switches. But to explain them: The SampleInterval switch will set the interval time between the 2 measuring samples, default is 1 second, the MaxSamples switch will give you an x amount of samples; the default value is 1 sample.

The output is not quite what I want, I only like to get the CounterSamples. This is achieved with this command:

(Get-Counter "\Process(Chrome*)\% Processor Time").CounterSamples

This’ll give me this output (with a lot of open tabs in chrome):

CpuUsage

But now, there’s the problem that the name as being used in the Performance Counters isn’t the same as in the process list, neither do I know a process ID. Lets solve this with the Get-Counter command as well. This command will show me the Process ID’s:

(Get-Counter "\Process(Chrome*)\ID Process").CounterSamples

And will give me this list of ID’s:

CpuUsageID

Now it’s time to start working with the data. I’ll start by putting the data in variables, then I’ll check if the lists are the same size. Once that’s done, I’ll store the data that I want and return the list sorted with the process with the highest CPU usage on top:

Param([parameter(Mandatory=$True)][string]$Process)
$ProcessId = (Get-Counter "\Process($Process*)\ID Process").CounterSamples | Sort-Object Path
$ProcessCPU = (Get-Counter "\Process($Process*)\% Processor Time").CounterSamples | Sort-Object Path,CPU
$TotalInfo = @()
If($ProcessId.Count -eq $ProcessCPU.Count)
{
    For($i = 0; $i -lt $ProcessId.Count; $i++)
    {
        $TempInfo = $ProcessId[$i].Path.Split("\")[3].Substring($ProcessId[$i].Path.Split("\")[3].IndexOf("(") + 1)
        $TempInfo = $TempInfo.Substring(0,$TempInfo.IndexOf(")"))
        $ProcessInfo = New-Object psobject
        Add-Member -InputObject $ProcessInfo -MemberType noteproperty -Name "CounterName" -Value $TempInfo -Force
        Add-Member -InputObject $ProcessInfo -MemberType noteproperty -Name "Id" -Value $ProcessId[$i].CookedValue -Force
        Add-Member -InputObject $ProcessInfo -MemberType noteproperty -Name "Cpu" -Value $ProcessCPU[$i].CookedValue -Force
        $TotalInfo += $ProcessInfo
    }
}
Return $TotalInfo | Sort-Object Cpu -Descending

And this will give me a nice list like this:

CpuUsageAndId

Oh, if you’re interested in getting the total CPU load of a system, this is also pretty easy with the Get-Counter command. Powershell.com also describes this, but can be done with this command:

(Get-Counter '\processor(_total)\% processor time').CounterSamples

The script can be downloaded here.

Get logged on users and sessions

There are several ways to get a list of currently logged on users on a system, but only a few return the things that I like to know. In case of my servers, I’d like to know which users are connected to which session.

One way to do so is by:

Import-Module RemoteDesktop
Get-RDUserSession

but this doesn’t give a consistent return (on some of my 2012 R2 servers it doesn’t return anything, even though users are logged on to the system and RDS is setup) and it doesn’t include the 0 session user(s)

GetUsersAndSessions1

WMI can also do the job, but by default will give a lot of unnecessary information

Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique | %{"{0}\{1}" -f $_.Antecedent.ToString().Split('"')[1],$_.Antecedent.ToString().Split('"')[3]}

(It’ll return DWM-1, DWM-2, DWM-3, IUSR, SYSTEM and more things I don’t need to know or work with) To filter most unwanted items, you’d get a big command like:

Get-WmiObject Win32_LoggedOnUser | Select Antecedent -Unique | Where-Object { $_.Antecedent.ToString().Split('"')[1] -ne $env:COMPUTERNAME -and $_.Antecedent.ToString().Split('"')[1] -ne "Window Manager" -and $_.Antecedent.ToString().Split('"')[3] -notmatch $env:COMPUTERNAME } | %{"{0}\{1}" -f $_.Antecedent.ToString().Split('"')[1],$_.Antecedent.ToString().Split('"')[3]}

This’ll return a list of all users (including administrator), but no sessions:

GetUsers

So until now the first command would be the best; but since it’s pretty inconsistent wether it indeed returns a list of users, I thought of ‘misusing’ another command for this purpose. The Get-process command. Since every logged in user has several processes in their name, this might be the best approach:

Get-Process -IncludeUserName | Select-Object UserName,SessionId | Where-Object { $_.UserName -ne $null } | Sort-Object UserName -Unique

This will return a list of all users, but users that are connected to multiple sessions, will only show in one this way. To get the list of users based on the session and filter out the accounts like DWM-1 to 3 etc, the following command  The Where-Object { $_.UserName -ne $null } part will make sure the UserName field is filled when walking through it, otherwise you’d get lots of errors on not being able to call a method on a mull-valued expression. Thus to rule these out, this part is added.

Get-Process -IncludeUserName | Select-Object UserName,SessionId | Where-Object { $_.UserName -ne $null -and $_.UserName.StartsWith("DOMAIN OR COMPUTERNAME") } | Sort-Object SessionId -Unique

This will return an object with UserName and SessionID for that user. Once multiple users are connected to the same session (like session 0), the filter for the domain name or computer name is needed to get the list of names you’d be interested in. Here is a screenshot of the result of the last command:

GetUsersAndSessions

The IncludeUserName switch has been added since Powershell 4.0, thus older versions will not be able to use this switch. Also the switch requires this command to be ran elevated; otherwise you’d get an error.

In the end I will definitely use the Get-Process command to get a consistent list of logged on users on my servers.

Edit: By the way, if you’re about to run this on a remote computer, wrap an Invoke-Command around it. Because if you include the -ComputerName switch, the -IncludeUserName switch can’t be combined. Next to that, the Get-Process command with -ComputerName will return all processes as running on session 0.

Thus if you are to use it on a remote computer, use a command like:

Invoke-Command -ComputerName "COMPUTERNAME" -ScriptBlock { Get-Process -IncludeUserName | Select-Object UserName,SessionId | Where-Object { $_.UserName -ne $null -and $_.UserName.StartsWith("DOMAIN OR COMPUTERNAME") } | Sort-Object SessionId -Unique } | Select-Object UserName,SessionId

Note that in the end there’s an extra Select-Object, because the Invoke-Command will add 2 extra properties to the result; PSComputerName and RunspaceId. You can also add credentials to this command and other parameters, depending on your needs.