April 2011 - Posts

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.

Posted by RichardSiddaway | with no comments
Filed under: ,

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

Posted by RichardSiddaway | with no comments

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

Posted by RichardSiddaway | with no comments
Filed under:

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

Posted by RichardSiddaway | with no comments
Filed under: ,

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

Posted by RichardSiddaway | with no comments
Filed under:

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.

Posted by RichardSiddaway | with no comments
Filed under:

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

Posted by RichardSiddaway | with no comments

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!!

Posted by RichardSiddaway | with no comments
Filed under: ,

Scripting Games 2011 Commentary: II–using a switch parameter

A switch parameter does exactly what it says. It switches on some piece of code in your function.

Using a switch parameter seems to be misunderstood.  I saw a lot of code like this during the games.

 

001
002
003
004
005
006
007
008
009
010
011

function testswitch {
param ([switch]$all=$false)
 
 if ($all -eq $false){
   get-process | sort CPU -Descending | select -First 5
 }
 else {
   get-process | sort CPU -Descending
 } 

}

 

Our function is designed to display the top 5 CPU using processes currently running on the system. That is the default behaviour.

A switch parameter is defined. If the switch is used we get all processes sorted by CPU usage.

This code will work but it involves more coding than is necessary. This can introduce bugs – more typing = more chance for mistakes (especially for me)

There are a few things to remember:

  • switches are boolean (true of false)
  • switch paramters default to false – when we use the switch its value changes to true
  • if statement tests have to produce a boolean result

This means we can change our code to this

 

001
002
003
004
005
006
007
008
009
010
011

function testswitch2 {
param ([switch]$all)
 
 if ($all){
   get-process | sort CPU -Descending 
 }
 else {
   get-process | sort CPU -Descending | select -First 5
 } 

}

 

We leave the switch parameter to default to false

The test on the if statement produces a boolean – $all is a boolean so we can test its value directly.

The order of the statements is changed slightly. If $all is true (switch is used) then we produce all results otherwise we produce just first 5

I always find it easier to work with an if statement where the first resulting script block occurs if the test is true.  We could write the if block as

 

if (-not($all)){
  get-process | sort CPU -Descending | select -First 5
}
else {
  get-process | sort CPU -Descending } 

but I find that more confusing when I come back to it.

 

Points to remember:

  • switches are boolean and default to false
  • can test booleans directly with if  - - if ($variable){. . . }
Posted by RichardSiddaway | with no comments
Filed under:

Scripting Games 2011 Commentary: I–Exporting after using a format cmdlet

As the final judging of the Scripting Games takes place today I thought it time to blog about a few things I noticed while judging.  These comments are not about solving the events but about using PowerShell.  These comments are in no particular order – just things I observed.

This is a standard pipeline. I would suggest that you try these snippets to see what is happening.

Get-Process | select -First 3 | Format-Table

There are a number of ways to get this information in to a file

Get-Process | select -First 3 | Export-Csv test1.csv –NoTypeInformation

This stores the whole objects worth of data. Using –NoTypeInformation means that it is returned as a PSCustomObject when we import it.

Alternatively we can do this

Get-Process | select -First 3 | Out-File test1.txt

This stores just what we would have seen on screen – ie the results of the default display

PS> get-content test1.txt

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     57       3     1764       5196    58     1.64    808 conhost
     30       2      504       2124    20            1404 conhost
     57       3     1760       5156    54     0.20   1688 conhost

When we read the file contents we get a collection of strings.  All object information is lost.

 

Get-Process | select -First 3 | Format-Table | Out-File test2.txt

is equivalent to our out-file example above in that we get strings

 

Get-Process | select -First 3 | Format-Table | export-csv test2.csv

Outputs the formatting objects NOT the data.  Oops.

 

When exporting data to files make sure you are getting what you expect. Don’t export after a format statement. Use Export-Csv if you want objects and Out-File if you just want the data points.

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell and Shakespeare

Not a title you see very often I admit but if the Bard had been at Deep Dive this is what he would have said about it:

 

We few, we happy few, we band of brothers;
For he to-day that shares his script with me
Shall be my brother; be he ne'er so vile,
This day shall gentle his condition;
And gentlemen in England now-a-bed
Shall think themselves accurs'd they were not here,
And hold their manhoods cheap whiles any speaks
That were with us upon PowerShell Deep Dive 1.

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell and WMI MEAP update

Chapters 8 and 9 of PowerShell and WMI have been released into the MEAP. They are available from http://www.manning.com/siddaway2/

 

Chapter 8 covers the File system

  • Administer shares
  • Compress or encrypt files
  • Monitor file system events

Chapter 9 covers Services and processes including:

  • service load order
  • discover process owners
  • use WMI events to control processes

The code from the chapters is available or download.

Next up is chapter 10 dealing with printers.

Chapter 11 on Networking is complete and I’m working on the IIS WMI provider (chapter 12) and configuring a new server (chapter 13)

Enjoy

Posted by RichardSiddaway | with no comments

PowerShell Deep Dive–Sunday

Arrived in sunny Las Vegas.  Conference proper starts tomorrow but there is a real interest in PowerShell.  Already had lots of conversations and the breadth of stuff people are using it for amazes me. 

There are some awesome sessions coming up.  Look for info filtering into blog posts in the near future.

Posted by RichardSiddaway | with no comments
Filed under:

Tuesday’s recording

The recording from Tuesdays UG meeting is available.

Richard Siddaway has invited you to view a Microsoft Office Live Meeting recording.
View Recording
Recording Details
Subject: PowerShell and COM objects
Recording URL: https://www.livemeeting.com/cc/usergroups/view
Recording ID: Q4DPJT
Attendee Key: 4~_TzB%6w

 

The slides and scripts are available from

http://cid-43cfa46a74cf3e96.office.live.com/browse.aspx/PowerShell%20User%20Group/2011%20April

Previous meeting’s recordings are still available:

  • Regular Expressions
  • PowerShell utility cmdlets
  • PowerShell best practice
  • Remoting
  • Registry
  • DNS
  • Events
  • Modules

Scripting Games entries

With only 7 events announced there have been more than 1000 scripts entered into the Games – still plenty of time to join in

Posted by RichardSiddaway | with no comments
Filed under:

Scripting Games 2011 commentary I: Line continuations

No I’m not going to give you the answers – you’ll just have to wait until the events close.

During (and possibly after) the games I’m going to comment on some of the things I’ve noticed about PowerShell usage.

I want to start with breaking a PowerShell line across multiple lines.  The “one liner” is what many PowerShell users aspire to and we can put together some impressive functionality by stringing together some cmdlets using the pipeline – for instance:

Get-Process | sort CPU -Descending | select -First 6 | Format-Table –AutoSize

 

Now we’ll assume we need to break this so that it fits on shorter lines.  The back tick is the line continuation character so we could end up with this

Get-Process | `
sort CPU -Descending | `
select -First 6 | `
Format-Table –AutoSize

which is actually easier to read.  Another alternative could be this

Get-Process `
| sort CPU -Descending `
| select -First 6 `
| Format-Table –AutoSize

which again is easy to read.  The difference is in the positioning of the back tick before or after the pipe symbol.

 

Both of these options take more effort than required.  All we need to do is this

Get-Process |
sort CPU -Descending |
select -First 6 |
Format-Table –AutoSize

The pipeline symbol works as a line continuation symbol as well.  Save typing and make life easier.

Enjoy!

Posted by RichardSiddaway | with no comments
Filed under:
More Posts Next page »