April 13, 2012 5:20 AM
Posted by: Richard Siddaway
PowerShell
I have seen a few interesting variations on using where-object during the games. This is normally aliased to where. You can use ? if you are fanatical about aliases but to me your PowerShell starts to become unreadable if its heavily aliased.
Consider get-process. It returns a set of process objects. Where-Object is used to put a filter on those objects to cut down the amount of data on the pipeline –
PS> get-process | where {$_.Handles -gt 500}
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
558 9 1576 3424 46 384 csrss
660 11 1768 6816 102 452 csrss
1396 38 45412 58896 262 40.69 2776 explorer
622 23 43196 63204 309 30.76 2164 iexplore
1000 17 4988 10440 36 508 lsass
509 14 37992 43308 202 4.06 4688 powershell
938 35 42864 27516 131 3620 SearchIndexer
628 16 16392 15968 90 824 svchost
803 18 65548 73660 160 864 svchost
1369 34 22884 34012 200 900 svchost
504 17 6616 11640 52 1056 svchost
784 0 48 968 3 4 System
669 50 109832 78000 400 17.99 1700 WindowsLiveWriter
526 18 17084 24020 150 4.06 788 wlcomm
1951 178 222312 217488 689 96.39 3872 wlmail
or
PS> get-process | where {$_.CPU -gt 50}
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
124 8 28828 24996 120 131.10 2744 dwm
485 23 14940 37444 202 52.49 3272 iexplore
363 26 25180 60088 335 78.14 5336 WINWORD
1951 178 222312 217488 689 96.42 3872 wlmail
We often need to combine filters. These can apply both filters or either filter.
Both filters are applied using the –and operator
PS> get-process | where {$_.CPU -gt 50 -and $_.Handles -gt 500 }
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
1956 179 222332 217508 689 96.47 3872 wlmail
In the situation where you want either the CPU or the Handles filter to apply you use the –or operator
PS> get-process | where {$_.CPU -gt 50 -or $_.Handles -gt 500 }
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
532 9 1576 3368 46 384 csrss
651 11 1768 6816 103 452 csrss
124 8 27804 23964 119 132.18 2744 dwm
1403 38 46744 60284 263 41.53 2776 explorer
619 23 43128 63196 308 30.76 2164 iexplore
488 23 14940 37444 202 52.51 3272 iexplore
992 17 4988 10440 36 508 lsass
566 14 34704 40004 202 4.68 4688 powershell
918 35 42860 27436 131 3620 SearchIndexer
617 16 16364 15952 89 824 svchost
807 18 65548 73684 160 864 svchost
1371 34 22932 34048 201 900 svchost
508 17 6616 11632 52 1056 svchost
782 0 48 968 3 4 System
672 50 103560 76708 400 32.85 1700 WindowsLiveWriter
363 26 25180 60088 335 78.16 5336 WINWORD
520 18 17060 24000 150 4.09 788 wlcomm
1953 178 222332 217508 689 96.52 3872 wlmail
Notice the very different outputs!
One thing I have seen in the games is the use of cascading where
PS> get-process | where {$_.CPU -gt 50 } | where {$_.Handles -gt 500 }
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
1953 178 222312 217496 689 96.52 3872 wlmail
logically this is equivalent of where {$_.CPU -gt 50 -and $_.Handles -gt 500 }
Is there any difference in speed
1..100 | foreach {
Measure-Command -Expression {get-process |
where {$_.CPU -gt 50 -and $_.Handles -gt 500 }}
} | Measure-Object -Average TotalMilliseconds
produces an answer of 153.92904 milliseconds
1..100 |
foreach {Measure-Command -Expression {get-process |
where {$_.CPU -gt 50 } | where {$_.Handles -gt 500 }}} |
Measure-Object -Average TotalMilliseconds
gives 152.905894 milliseconds
Not a massive difference. But does the order of the where statements make a difference?
1..100 | foreach {
Measure-Command -Expression {get-process |
where {$_.Handles -gt 500 -and $_.CPU -gt 50 }}} |
Measure-Object -Average TotalMilliseconds
produces an answer of 51.495388
1..100 | foreach {
Measure-Command -Expression {get-process |
where {$_.Handles -gt 500 | where {$_.CPU -gt 50 } }}} |
Measure-Object -Average TotalMilliseconds
gives 37.23507 milliseconds
Filtering on Handles first produces a quicker result because it is a heavier filter – it produces less out put so the second filter has less work to do. I wasn’t expecting such a big difference between the use of –and & the cascading where statements though – that was a surprise.
So what have learnt:
- where filter clauses can be combined with –and OR –or
- where filter clauses can be cascaded in multiple where statements
- putting the heaviest filter first speeds the process
- cascading where statements can be quicker than using –and
Be careful with the last statement and test any code before committing to using cascading wheres Also test the order of your filters very carefully.
April 12, 2012 2:46 AM
Posted by: Richard Siddaway
PowerShellSecond event is to find running services that can be stopped
http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/03/2012-scripting-games-beginner-event-2-find-stoppable-running-services.aspx
Lets start with the requirements we can pick out of the event information:
- Find all services that are running that will accept a Stop Command
- Need to run remotely against trusted remote computers
- Assume appropriate ports are open of firewalls
- Don’t need a second computer
- Don’t need to test or configure firewall – assume its done
- Use simplest command that will work
- Can use standard aliases – don’t have to
- Don’t use non-standard aliases
So what do we have that can work with services
PS> Get-Command *service
CommandType Name
———– —-
Cmdlet Get-Service
Cmdlet Invoke-WebService
Cmdlet New-Service
Cmdlet Restart-Service
Cmdlet Resume-Service
Cmdlet Set-Service
Cmdlet Start-Service
Cmdlet Stop-Service
Cmdlet Suspend-Service
Get-Service looks a likely contender and it has a computername parameter
PS> Get-Help Get-Service -Parameter c*
-ComputerName <string[]>
Gets the services 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 a remote computer. To specify the local 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-Service even if your computer is not configured to run remote commands.
Required? false
Position? named
Default value Localhost
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
Can we find out if services can be stopped
PS> get-service | Get-Member -MemberType property
TypeName: System.ServiceProcess.ServiceController
Name MemberType
—- ———-
CanPauseAndContinue Property
CanShutdown Property
CanStop Property
Container Property
DependentServices Property
DisplayName Property
MachineName Property
ServiceHandle Property
ServiceName Property
ServicesDependedOn Property
ServiceType Property
Site Property
Status Property
Looks like the Canstop will work for us. Its a Boolean i.e. only returns True or False so we have a little trick. Normally when we want to filter we use a where statement
get-service | where {$_.Status -eq "Running"}
The only objects that are passed along the pipeline are the ones where the filter evaluates to TRUE. So you might think this would work
get-service | where {$_.Canstop -eq "True"}
but check out this
PS> $true -eq "False"
True
What’s happening is that $_.CanStop is a Boolean so triggers the filter – the “-eq ‘True’” bit is ignored.
If you want to test Booleans use PowerShell’s $true & $false
get-service | where {$_.Canstop -eq $true}
or better still because CanStop is a Boolean we can just do a simple test
get-service | where {$_.Canstop}
To use the computer name
Get-Service -ComputerName $env:COMPUTERNAME | where {$_.Canstop }
see http://msmvps.com/blogs/richardsiddaway/archive/2012/04/11/scripting-games-2012-comments-3-beginners-event-1.aspx for a discussion on accessing the local machine using a computer name
At this point you may be asking but the scenario says we have to return running services that can be stopped. Look at the output when you use CanStop – all the statuses are running.
It can be tested like this
Compare-Object -ReferenceObject $(get-service | where {$_.Canstop}) -DifferenceObject $(get-service | where {$_.Status -eq "Running" -and $_.Canstop}) –IncludeEqual
You will get a bunch of answers like this
InputObject SideIndicator
———– ————-
System.ServiceProcess.ServiceController ==
Which means they match
What about remote machines
Get-Service -ComputerName "Win7", "Win7test", "webr201" |
where {$_.Canstop }
will work but we don’t know which service is returned from which machine however there is a machine name property available so we could do this
Get-Service -ComputerName "Win7", "Win7test", "webr201" |
where {$_.Canstop } |
select Status, Name, Displayname, MachineName
or if you want a nice report format
Get-Service -ComputerName "Win7", "Win7test", "webr201" |
where {$_.Canstop } |
sort MachineName, Name |
Format-Table -GroupBy MachineName –AutoSize
The alternative is to use PowerShell remoting
Invoke-Command -ComputerName "Win7", "Win7test", "webr201" -ScriptBlock {
Get-Service |
where {$_.Canstop } }
This automatically gives the computername so we can do this
Invoke-Command -ComputerName "Win7", "Win7test", "webr201" -ScriptBlock {
Get-Service |
where {$_.Canstop } } |
Format-Table -GroupBy PSComputerName –AutoSize
In PoweShell v3 the syntax can be simplified
Get-Service | where CanStop
When you have a single property to test you can drop the $_ and {}
This can be aliased to
gsv | ? canstop
which must be esoteric enough for the most fanatical of alias’ fans.
If you must the other versions can be aliased
gsv -cn "Win7", "Win7test", "webr201" |
? {$_.Canstop} |
select Status, Name, Displayname, MachineName
or
icm -Cn "Win7", "Win7test", "webr201" -Sc {
gsv |
? {$_.Canstop } } |
ft -G PSComputerName –A
April 11, 2012 7:47 AM
Posted by: Richard Siddaway
PowerShellI 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
http://blogs.technet.com/b/heyscriptingguy/archive/2012/04/02/the-2012-scripting-games-beginner-event-1-use-windows-powershell-to-identify-a-working-set-of-processes.aspx
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!
April 10, 2012 12:56 PM
Posted by: Richard Siddaway
PowerShellThe beginners section of the scripting games can be often answered with one pipeline of PowerShell.
Notice that I stated one pipeline not one line
Since the early days of PowerShell there has been an almost mystical reverence paid to the concept of the “one-liner” ie boiling the PowerShell script down to a single line. That worked as a concept in PowerShell v1 where all we had was the console. When PowerShell v2 appeared with the ISEit was time to ditch the one line concept and switch our attention to what is important – using the PowerShell pipeline.
The pipeline symbol acts as a line continuation character so in ISE or any other PowerShell aware editor/environment you can split your pipeline over multiple lines of text BUT STILL HAVE A SINGLE PIPELINE.
Its more readable and easier to work with especially when debugging.
With PowerShell v3 and the changes to ISE it is finally time to kill the myth of the one liner and actually be honest about what we are trying to achieve – the one pipeline.
In a similar vein there is a lot of talk about how we should use aliases at the command prompt.
Why?
With tab completion it is almost as quick to use the full name for cmdlets and parameters especially if you have to look up the alias. If you like them fine but I prefer not to use them so that what ever I experiment with at the prompt can be copied straight into scripts.
April 9, 2012 2:44 AM
Posted by: Richard Siddaway
PowerShellI’m seeing a lot of scripts where code is used to perform an action that is available as a parameter on a cmdlet that is used earlier in the pipeline.
Read the help file for the cmdlets you are going to use to see how you can get PowerShell to do the work for you instead you having to create and test code
April 8, 2012 7:54 AM
Posted by: Richard Siddaway
PowerShellAs with previous games I’ll publish a series of comments on the games. I won’t deal with any event specific issues until the games events are all closed but there are some things coming through that while not affecting any gradings don’t make the best use of PowerShell.
The first one that’s jumped out is line continuation.
I’ve seen a number of examples like the following two
get-service | `
sort status -Descending
or
get-service `
| sort status -Descending
Both work but the line continuation character is unnecessary
All you need to do is
get-service |
sort status -Descending
The pipeline symbol also works as a line continuation.
A minor point maybe but let PowerShell do the work for you
April 6, 2012 7:32 AM
Posted by: Richard Siddaway
PowerShellWe’re just about at the halfway point with 5 events available – remember there is only a week to complete an event. Deadlines start on Monday.
The overall quality of submissions from what I’ve seen so far is up on last year which is an achievement. What is staggering is that we’ve already had as many entries as we had in total last year.
Its going to be a busy time for the judges 
April 2, 2012 12:39 PM
Posted by: Richard Siddaway
PowerShellJump over to http://blogs.technet.com/b/heyscriptingguy/
for event 1 in the beginners and advanced categories
April 2, 2012 12:33 PM
Posted by: Richard Siddaway
DNS,
PowerShell,
WMIA forum question asked how to find the primary name from an alias or CNAME record.
Get-WmiObject -Namespace 'root\MicrosoftDNS' -Class MicrosoftDNS_CNAMEType `
-Filter "ContainerName = 'Manticore.org'" -ComputerName server02 |
select @{N='Alias'; E={$_.Ownername}}, Primaryname
use the MicrosoftDNS_CNAMEType class. Filter on the domain ie containername. ComputerName holds the DNS server name.
Change OwnerName to Alias in a select calculated field