I thought I’d publish a series on how I would go about solving these plus some comments and observations on the submitted scripts.
First event of the games
Firstly pick out the salient points of the event
Need to:
- Identify Top 10 processes consuming memory resources on each computer
- Decided that working set is the property to measure
- Don’t need a script – implies a single pipeline will be acceptable
- Should be capable of running remotely – not required to have multiple computers available
- Aliases are acceptable – not compulsory though (we’ll come back to that statement)
- Should return an object
- Should be able to write results to file (but not a requirement)
- Computers are Windows 7 & Windows 2008 R2
- Single AD domain
- Remoting is enabled
What do we have that can work with processes:
PS> Get-Command *process
CommandType Name
———– —-
Cmdlet Debug-Process
Cmdlet Get-Process
Cmdlet Start-Process
Cmdlet Stop-Process
Cmdlet Wait-Process
Get-Process should work for us but it returns a whole bunch of processes and none of them are labelled working set! If we need to be able to find the top 10 working set consumers then need to be able to sort on working set
Time to use Get-Member
PS> Get-Process | Get-Member w* -MemberType property
TypeName: System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
WorkingSet Property System.Int32 WorkingSet {get;}
WorkingSet64 Property System.Int64 WorkingSet64 {get;}
One thing to be aware of is that sometimes the PowerShell team alias property names to make life easier for us. You also need to be aware that the column WS (K ) is workingset recast to kb instead of bytes
PS> Get-Process | Get-Member w* -MemberType aliasproperty
TypeName: System.Diagnostics.Process
Name MemberType Definition
—- ———- ———-
WS AliasProperty WS = WorkingSet
So we have a choice – Workingset or WS its alias. Also we have to assume that the boss meant workingset even on 64 bit machines where WorkingSet64 may be a better answer
So lets sort on workingset
Get-Process | sort Workingset
This gives us the processes sorted in ascending order of working set usage. At this point we could just select the last 10
Get-Process | sort Workingset | select -Last 10
but that still leaves the results in ascending order which isn’t the way we usually look at things. So lets turn it round
Get-Process | sort Workingset -Descending | select -First 10
This gives us the top 10 consumers of working set on the local machine.
I have used aliases for sort-object (sort) and select-object (select) for a couple of reasons:
- its easier
- its still simple to read
- its less typing
- its accepted convention
I have left the rest of the script as full names so that if I save as script I can still read it. Aliases are great at the command line if, and only if, you can remember them. Its better to use the full name and get the job done compared to hunting for the alias because its interactive work and some one says use aliases at the prompt.
If you are learning PowerShell stick with the full names (tab completion is a big help) until you are confident with a cmdlet – then introduce using aliases BUT ONLY FOR INTERACTIVE WORK.
So we can get the result for the local machine. What about writing to a file?
Couple of ways to do that
Get-Process | sort Workingset -Descending | select -First 10 | Out-File top10proc.txt
gives the results in a text file – viewable with
Get-Content top10proc.txt
or use a csv file
Get-Process | sort Workingset -Descending | select -First 10 | Export-Csv top10proc.csv -NoTypeInformation
Import-Csv top10proc.csv
But notice you get the process information back formatted as a list with ALL of the properties presented.
If you want to be very clever here use tee-object which sends objects to a file and along the pipeline
Get-Process | sort Workingset -Descending | select -First 10 | Tee-Object -FilePath top10proc.txt
You get the display on screen and the data saved in a file
What about remote systems?
All your machines are in a domain for which you are the admin so presumably you have suitable permissions across all systems. Plus all machines are Windows 7 or Windows 2008 R2 so have PowerShell v2 available AND remoting is enabled.
There are two ways to approach this:
- use the –computername parameter on get-process
- use PowerShell remoting – probably invoke-command because we are only visiting a machine once
When you use the computername parameter and attempt to access the local machine there are three ways of accessing it without typing the name
Get-Process -ComputerName . | sort Workingset -Descending | select -First 10
Get-Process -ComputerName localhost | sort Workingset -Descending | select -First 10
Get-Process -ComputerName $env:COMPUTERNAME | sort Workingset -Descending | select -First 10
The second one doesn’t work on Windows 7 but does work on Windows 2008 R2. There are other cmdlets that have problems with localhost so I advise against using it. My preference is to use $env:COMPUTERNAME
You might try this
“Win7″, “Win7Test”, “WebR201″ |
Get-Process |
sort Workingset -Descending |
select -First 10
Note its still one pipeline – just split across multiple lines to make reading easier
But you’ll get an error
Get-Process : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of
the parameters that take pipeline input.
The problem is that the computername will only take pipeline input based on property name – so it expects an object that will have a computername property.
PS> get-help Get-Process -Parameter computername
-ComputerName <string[]>
Gets the processes running on the specified computers. The default is the local computer.
Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more computers. To specify the loc
al computer, type the computer name, a dot (.), or “localhost”.
This parameter does not rely on Windows PowerShell remoting. You can use the ComputerName parameter of Get-Process
even if your computer is not configured to run remote commands.
Required? false
Position? named
Default value
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
You can get round that like this
Get-Process -ComputerName “Win7″, “Win7Test”, “WebR201″
sort Workingset -Descending |
select -First 10
but you then hit the problem that you can’t differentiate the results from each machine.
You also need to have a number of services running on the remote machine including the Remote Registry service AND the firewall configured to allow remote management.
This is starting to get messy.
Remember that remoting was enabled across the domain. This means we can do this
Invoke-Command -ComputerName “Win7″, “Win7Test”, “WebR201″ -ScriptBlock {
Get-Process |
sort Workingset -Descending |
select -First 10 }
Still a single pipeline. The big advantage is that we get a property – PSComputerName – tacked onto the output that enables us to decide on the source of the data. That should answer all of requirements in the event. The computer names could be fed to Invoke-Command through a file or pipeline.
Change the script to this
Invoke-Command -ComputerName “Win7″, “Win7Test”, “WebR201″ -ScriptBlock {
Get-Process |
sort Workingset -Descending |
select -First 10 } |
Format-Table -GroupBy PSComputerName –AutoSize
and you have a nicely formatted output. Push that to a file
Invoke-Command -ComputerName "Win7", "Win7Test", "WebR201" -ScriptBlock { Get-Process | sort Workingset -Descending | select -First 10 } | Format-Table -GroupBy PSComputerName -AutoSize | Out-File top10proc.txt
and you have a report ready to give to the boss.
Jobs a goodun!
Oh you want aliases. Well if you must
icm -Cn “Win7″, “Win7Test”, “WebR201″ -Sc {
ps |
sort WS -Des |
select -F 10 }
trying to alias or abbreviate parameters can be problematic because the abbreviation must be resolvable unambiguously. Unless you really know the parameters on a cmdlet it can be confusing and take extra time to get right – its why I never bother and just use tab completion!