Monthly Archives: April 2011

PowerShell Deep Dive: IV Formatting script

During Jeff Hicks’ talk about formatting he did the usual demo of taking an extract of a format file and modify it to create a new default format. 

Jim Truher mentioned that he had a script that would generate the XML for a format file and that he would post it.  He has and its here

http://jtruher3.wordpress.com/2011/04/19/a-tool-for-table-formatting/

 

If you want to modify the way things are displayed or create you own standard formats this will make life much easier.

PowerShell Deep Dive III: WQL query speed

One topic that came up during my talk at Deep Dive was the speed of running a WQL vs using –Filter in Get-WmiObject.  I’d never tested it so its time to find out.

PowerShell v2 has a handy cmdlet called Measure-Command that times how long a command runs

We’ll start with using a filter

Get-WmiObject -Class Win32_Process -Filter "Name='Notepad.exe'"

 

if we wrap it in Measure-Command we get this

Measure-Command -Expression {Get-WmiObject -Class Win32_Process -Filter "Name='Notepad.exe'"}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 81
Ticks             : 817436
TotalDays         : 9.46106481481481E-07
TotalHours        : 2.27065555555556E-05
TotalMinutes      : 0.00136239333333333
TotalSeconds      : 0.0817436
TotalMilliseconds : 81.7436

 

We want the TotalMilliseconds property and we need to do it more than once

 

1..100 | foreach {Measure-Command -Expression {Get-WmiObject -Class Win32_Process -Filter "Name='Notepad.exe'"}} |

Measure-Object -Property TotalMilliseconds -Average


Count    : 100
Average  : 52.640332
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

Now lets repeat as a query

Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='Notepad.exe'"

 

which becomes

Measure-Command -Expression {Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='Notepad.exe'"}

 

1..100 | foreach {Measure-Command -Expression {Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE Name='Notepad.exe'"}} |

Measure-Object -Property TotalMilliseconds -Average


Count    : 100
Average  : 52.345972
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

Just for fun lets try this

1..100 | foreach {Measure-Command -Expression {Get-WmiObject -Class Win32_Process | Where {$_.Name -eq 'Notepad.exe'} }} |

Measure-Object -Property TotalMilliseconds -Average


Count    : 100
Average  : 92.96794
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

So the results so far

Filter:  52.640332

Query:   52.345972

Where:   92.96794


The filter and the query are almost the same – I’m not going to argue over 0.03 milliseconds.  Using Where-Object takes nearly twice as long. This is understandable because the query and filter pick out a single process but using Where-Object we return all processes and then filter.

I stated in my talk that it was better to use the filter because it was less typing. On these results I’ll stand by that statement for local machines as it takes me more than a few milliseconds to type the extra characters using a query.

Further research is needed:

  1. What happens if running against remote machines?
  2. is it faster to select properties in the query or using select-object

We’ll return to these points later

Scripting Games commentary: VI Get-Process

One of the events involved getting some information about a file running in a particular process. On a local machine we can use

Get-Process powershell –FileVersionInfo

 

Get-Process has a computername parameter so we can work remotely. 

 

WRONG.

If you try this against remote machine

PS> Get-Process powershell -FileVersionInfo -ComputerName server02
Get-Process : Exception getting "Modules" or "FileVersion": "Feature is not supported for remote machines.".
At line:1 char:12
+ Get-Process <<<<  powershell -FileVersionInfo -ComputerName server02
    + CategoryInfo          : InvalidOperation: (System.String[]:String[]) [Get-Process], InvalidOperationException
    + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.GetProcessCommand

 

You get an error.

If you look in the help file there is nothing to state that this will happen.

One often overlooked fact is that Get-Help has an online parameter that links to the Microsoft web site.  The PowerShell team update the online help when documentation issues come to light.  if you use

Get-Help Get-Process –Online

 

You will see that the online help has been updated to reflect the problem with FileVersionInfo

If you want to use it against a remote computer you will need to wrap it in Invoke-Command (best for a one off job) or build a remoting session

Invoke-Command -ScriptBlock {Get-Process powershell -FileVersionInfo} -ComputerName server02

PowerShell Deep Dive: II Win32_Volume

One question that I was asked at the deep dive -

Is there a way to link a disk volume back to the physical disk it resides on?

There doesn’t seem to be. If we test the WMI classes associated with a volume we get these results

Win32_Directory
Win32_QuotaSetting
Win32_ShadowProvider
Win32_ShadowCopy
Win32_ComputerSystem
Win32_Volume
Win32_Group

If anyone knows how to relate Win32_Volume to the physical disk (need to get the serial number off the disk) then I’d be interested in hearing about it

Scripting Games Commentary: V – Remoting

In PowerShell we have a number of ways to perform actions on remote machines. Many of the scenarios in the Scripting Games introduced the need to perform some action on a remote machine.

Many of the solutions involved creating sessions, using invoke-command and then tearing down the sessions.

WHY?

The problem is to get a few bits of information off the remote machine. Do you know PowerShell is installed on the remote machine? It isn’t installed on all the machines in my environment.

There is a quick remoting technique for situations like this. We don’t want to, or can’t, establish a remote session. Look at the cmdlets with a –ComputeName parameter. These can work with remote machines without needing to configure PowerShell remoting. Which cmdlets? Glad you asked.

PS> Get-Help * -Parameter Computer* | format-wide


Get-WinEvent                                                Get-Counter
Test-WSMan                                                  Invoke-WSManAction
Connect-WSMan                                               Disconnect-WSMan
Get-WSManInstance                                           Set-WSManInstance
Remove-WSManInstance                                        New-WSManInstance
Invoke-Command                                              New-PSSession
Get-PSSession                                               Remove-PSSession
Receive-Job                                                 Enter-PSSession
Get-EventLog                                                Clear-EventLog
Write-EventLog                                              Limit-EventLog
Show-EventLog                                               New-EventLog
Remove-EventLog                                             Get-WmiObject
Invoke-WmiMethod                                            Get-Process
Remove-WmiObject                                            Register-WmiEvent
Get-Service                                                 Set-Service
Set-WmiInstance                                             Get-HotFix
Test-Connection                                             Restart-Computer
Stop-Computer

All of these cmdlets can access remote machines directly. Use them rather than remoting for the simple jobs they were designed for.

Scripting Games 2012

Next years Scripting Games will be April 2-13

Looking forward to it already

Scripting Games Commentary: IV Whatif

PowerShell cmdlets that change the system state have a –whatif parameter to test what would happen for example

PS> Get-Service sp* | Stop-Service -WhatIf
What if: Performing operation "Stop-Service" on Target "Print Spooler (Spooler)".
What if: Performing operation "Stop-Service" on Target "Software Protection (sppsvc)".
What if: Performing operation "Stop-Service" on Target "SPP Notification Service (sppuinotify)".

 

I saw a number of entries where people were creating their own functions to supply whatif parameters. 

STOP.

This is functionality built into advanced functions

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017

function test-whatif {
[CmdletBinding(SupportsShouldProcess=$true)]
param (
 [parameter(ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true)]
 [string]$name
)

PROCESS{
  if ($psCmdlet.ShouldProcess("$name", "Stopping Service")) {
    "Service $name will be stopped"
  }
 
 
}#process

}

 

The important part is [CmdletBinding(SupportsShouldProcess=$true)]   This sets up the use of –whatif.  -Confirm we’ll look at another time

This is the working bit

if ($psCmdlet.ShouldProcess("$name", "Stopping Service")) {
    "Service $name will be stopped"
}

Check to see if  Shouldprocess is called – the parameters are the object the actions is performed on and the action that is being performed respectively (often a cmdlet name) – is it is print the whatif message otherwise perform the normal action

It is used like this

PS> Get-Service sp* | test-whatif
Service Spooler will be stopped
Service sppsvc will be stopped
Service sppuinotify will be stopped

 

No –whatif parameter so we get the action – in this case just a message

if we supply –whatif

PS> Get-Service sp* | test-whatif -WhatIf
What if: Performing operation "Stopping Service" on Target "Spooler".
What if: Performing operation "Stopping Service" on Target "sppsvc".
What if: Performing operation "Stopping Service" on Target "sppuinotify".

We get a What if:….      message

Huge piece of functionality for practically zero code.

Scripting Games Commentary: III PowerShell and Excel

Some of the events had the production of a CSV file as the end result with a bonus point if you opened the file in Excel.

The quickest way and easiest way to open a CSV file in Excel is like this

Invoke-Item -Path chapt4.csv

 

This will work if you have the CSV file extension opened with Excel.  If you don’t you need to fall back on opening Excel first

$xl = New-Object -ComObject "Excel.Application"
$xl.WorkBooks.Open("C:\Scripts\chapt4.csv")
$xl.Visible = $true

 

If at all possible create your data in a CSV file and then open in Excel. Trying to populate an Excel spread sheet from PowerShell is SLOOOOOOOOOOOOOOOOOW.

Be aware that if you want to work directly with Excel there is a bug in Excel 2007 and earlier.

You would expect to be able to do this

$xl = New-Object -comobject "Excel.Application"
$xl.visible = $true
$xlbooks =$xl.workbooks
$wkbk = $xlbooks.Add()

 

It works if your system is set to en-US culture otherwise it fails.  In Excel 2010 it works for some cultures such as en-GB but still fails for others.  The way round it is to open Excel like this

$xl = New-Object -Comobject "Excel.Application"
$xl.Visible = $true
$xlbooks =$xl.Workbooks
$newci = [System.Globalization.CultureInfo]"en-US"
$wkbk = $xlbooks.PSBase.GetType().InvokeMember("Add", [Reflection.BindingFlags]::InvokeMethod, $null, $xlbooks, $null, $newci)

 

Its slightly more painful but works for all versions and all cultures that I am aware of

May 2011–UK PowerShell UG


When: Tuesday, May 10, 2011 8:30 PM (BST)


Where: Live Meeting

*~*~*~*~*~*~*~*~*~*


Join PowerShell MVP and author Jonathan Medd to learn about PowerShell modules and how to get the most out of them.

Notes



Richard Siddaway has invited you to attend an online meeting using Live Meeting.
Join the meeting.
Audio Information
Computer Audio
To use computer audio, you need speakers and microphone, or a headset.
First Time Users:
To save time before the meeting, check your system to make sure it is ready to use Microsoft Office Live Meeting.
Troubleshooting
Unable to join the meeting? Follow these steps:

  1. Copy this address and paste it into your web browser:
    https://www.livemeeting.com/cc/usergroups/join
  2. Copy and paste the required information:
    Meeting ID: 8TWQGF
    Entry Code: 6NB,TJm(m
    Location: https://www.livemeeting.com/cc/usergroups

If you still cannot enter the meeting, contact support

Notice
Microsoft Office Live Meeting can be used to record meetings. By participating in this meeting, you agree that your communications may be monitored or recorded at any time during the meeting.

PowerShell Deep Dive: I–COM collections

The PowerShell Deep Dive last week was the best conference I have ever attended. The group consisted of members of the PowerShell team, Ed Wilson – the Scripting Guy, PowerShell MVPs and a large number of PowerShell experts and enthusiasts.  The last word summed it up.  The Scripting Club was supposed to finish at 10pm – it really finished at 3am next morning.

The sessions were short – 35 minutes – and much more interactive than a normal conference. A blog post can’t do justice to the event all I can say is that I’ll be booking for the next one as soon as its announced.

With that many experts drawn together I managed to learn something in every session – even the one I gave!

I’ll post some snippets of I what I learned in a series of posts. Some you may know & some may be new.

The first snippet involves working with COM collections. Many COM objects have properties that are actually collections of other objects. As an example consider the Windows firewall.

$fw = New-Object -ComObject HNetCfg.FwMgr

If we drill into the object we find

$fw.LocalPolicy.CurrentProfile.Services

on my machine I get three services

- File and Printer Sharing
- Network Discovery
- Remote Desktop

The logical thing to try would be

PS> $fw.LocalPolicy.CurrentProfile.Services[0]
Unable to index into an object of type System.__ComObject.
At line:1 char:41
+ $fw.LocalPolicy.CurrentProfile.Services[ <<<< 0]
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : CannotIndex

 

But we get an error.  The collection doesn’t have an index.  Bah!!!

But if we do this

$x = @($fw.LocalPolicy.CurrentProfile.Services)

 

we can then do this

PS> $x[0]


Name              : File and Printer Sharing
Type              : 0
Customized        : False
IpVersion         : 2
Scope             : 1
RemoteAddresses   : LocalSubnet
Enabled           : True
GloballyOpenPorts : System.__ComObject

 

which gives us an array where we can work with indexes

Simple but effective.

This is one of the strengths of an event like Deep Dive – you get to pick everyone else’s brain. During a session someone said “None of us know it all but between us in this room we know nearly all of it”

That should be the motto of the PowerShell community!!