PowerShell Feeds
Windows PowerShell Blog:
I wrote the previous blog in the middle of watching the video. The VERY next thing he did was to create 2 hashtables so my simple version of ConvertTo-HashTable.ps1 wouldn’t work for that case. So I made a more sophisticated version:
# ConvertTo-hashTable.ps1
# Updated to create multiple hashtables
param(
[string]
$key,
$value
)
Begin
{
$hashTables = @()
foreach ($v in @($value))
{
$hashTables += @{}
}
$Script = $false
if ($value -is [ScriptBlock])
{
$Script = $true
}
}
Process
{
$thisKey = $_.$Key
for ($i = 0 ; $i -le $hashTables.Count; $i++)
{
$hash = $hashTables[$i]
if (@($Value)[$i] -is [ScriptBlock])
{
$hash.$thisKey = & @($Value)[$i]
}
else
{
$hash.$thisKey = $_.$(@($Value)[$i])
}
}
}
End
{
foreach ($hash in $hashtables)
{
Write-Output $hash
}
}
I’ve attached the script as well.
What this does is to emit multiple hashtables - one for each VALUE you want. So the way you would use this is:
$ht1,$ht2 = gwmi Win32_logicalDisk –Filter “DriveType = 3” | ConvertTo-HashTable DeviceId {$_.size - $_.freeSpace}, FreeSpace
I’m going to go back and finish watching the video. I may end up with a couple more versions before I’m through. ha ha.
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
I´m currently watching an cool video of PoshBoard a PowerShell based Web Portal by Pilosite available on CodePlex. It looks great.
In the middle of the video I noticed the following script he uses to populate one of the controls on the dashboard:
$ht = @{ }
$objDrives = gwmi –class “Win32_logicalDisk” –Namespace “root\cimv2” |where {$_.DriveType -eq 3 }
foreach ($ObjDisk in $objDrives)
{
$size = $objDisk.size / 1024
$freespace = $objDisk.FreeSpace / 1024
$IntUsed = ($size - $freeSpace) * 1024
$ht.Add($objDisk.DeviceId, $IntUsed)
}
This is perfectly fine code but I noticed a couple opportunities for improvement:
- You don´t need to use quotes in: “Win32_logicalDisk” or “root\cimv2”
- The default namespace for gwmi is root\cimv2 so it it is redundant
- Gwmi supports –FILTER which can be dramatically more efficient so it is worth learning it´s different syntax and using it when you can
- CLASS can be a positional parameter so you don´t need to say –CLASS.
Those observations allow us to transform
$objDrives = gwmi –class “Win32_logicalDisk” –Namespace “root\cimv2” |where {$_.DriveType -eq 3 }
into
$objDrives = gwmi Win32_logicalDisk –Filter “DriveType = 3”
Next I noticed that it was using a foreach loop to create a hashtable. I´ve seen lots of code like this and in talking to a lot of new users, I´ve come the the conclusion that there are a set of beginner users that have a difficult time getting their heads around control structures (e.g. if/elseif/else, for, foreach, while, do/until). These folks seem to do just fine with commands and pipelines so I´m on the lookout for ways to make control structures option. In the spirit, I wrote ConvertTo-HashTable.ps1 :
# ConvertTo-hashTable.ps1
#
param(
[string]
$key,
$value
)
Begin
{
$hash = @{}
$Script = $false
if ($value -is [ScriptBlock])
{
$Script = $true
}
}
Process
{
$thisKey = $_.$Key
if ($script)
{
$hash.$thisKey = & $Value
}else
{
$hash.$thisKey = $_.$Value
}
}
End
{
Write-Output $hash
}
Once you have this (and you notice that you can get rid of the multiplying and dividing by 1024), we can transform this:
$ht = @{ }
$objDrives = gwmi –class “Win32_logicalDisk” –Namespace “root\cimv2” |where {$_.DriveType -eq 3 }
foreach ($ObjDisk in $objDrives)
{
$size = $objDisk.size / 1024
$freespace = $objDisk.FreeSpace / 1024
$IntUsed = ($size - $freeSpace) * 1024
$ht.Add($objDisk.DeviceId, $IntUsed)
}
into this:
$ht = gwmi Win32_logicalDisk –Filter “DriveType = 3” | ConvertTo-HashTable DeviceId {$_.size - $_.freeSpace}
That looks pretty readable to me.
BTW - here is the video
PowerShell Dashboard Web Portal Presentation from pilosite on Vimeo.
Enjoy!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
A while ago the System Center Virtual Machine Manager team showed me a chart of their cmdlets. It was an Excel spreadsheet which had the VERBs as rows and the NOUNs as columns. I thought that this was a wonderful way to look at the cmdlets. I woke up this morning and decided to write a script which would do something similar. I´ve attached that script which accomplishes that task. In the process, I decided that there is a good general purpose function here but I haven´t figured out what it´s name is so right now I´m just calling it ConvertTo-XXX (I´m open to suggestions). NOTE – the cmdlet requires the latest version of PowerShell (the stuff we released at the PDC) so if you don´t have those bits, you´ll have to wait a few more weeks until CTP3 is available to try it out. Also note that this is using yesterday´s build which may have more cmdlets than the PRE-Beta we release.
I´m experimenting with a few coding practices so some of these may stay and some may go. I´d love any feedback you have on that topic.
PS> .\ConvertTo-XXX -Test
Microsoft.PowerShell.Diagnostics
Name Counter WinEvent
—- ——- ——–
Export Export-Counter *
Get Get-Counter Get-WinEvent
Import Import-Counter *
Microsoft.WSMan.Management
Name WSMan WSManAction WSManCredSSP WSManInstance WSManQuickConfig WSManSessionOption
—- —– ———– ———— ————- —————- ——————
Connect Connect-WSMan * * * * *
Disable * * Disable-WSManCredSSP * * *
Disconnect Disconnect-WSMan * * * * *
Enable * * Enable-WSManCredSSP * * *
Get * * Get-WSManCredSSP Get-WSManInstance * *
Invoke * Invoke-WSManAction * * * *
New * * * New-WSManInstance * New-WSManSessionOption
Remove * * * Remove-WSManInstance * *
Set * * * Set-WSManInstance Set-WSManQuickConfig *
Test Test-WSMan * * * * *
Microsoft.PowerShell.Core
Name Command Console Help History Job Module ModuleManifest ModuleMember Object
—- ——- ——- —- ——- — —— ————– ———— ——
Add * * * Add-History * * * * *
Clear * * * Clear-History * * * * *
Disable * * * * * * * * *
Enable * * * * * * * * *
Enter * * * * * * * * *
Exit * * * * * * * * *
Export * Export-Console * * * * * Export-ModuleMember *
ForEach * * * * * * * * ForEach-Object
Get Get-Command * Get-Help Get-History Get-Job Get-Module * * *
Import * * * * * Import-Module * * *
Invoke Invoke-Command * * Invoke-History * * * * *
New * * * * * New-Module New-ModuleManifest * *
Receive * * * * Receive-Job * * * *
Register * * * * * * * * *
Remove * * * * Remove-Job Remove-Module * * *
Set * * * * * * * * *
Start * * * * Start-Job * * * *
Stop * * * * Stop-Job * * * *
Test * * * * * * Test-ModuleManifest * *
Unregister * * * * * * * * *
Wait * * * * Wait-Job * * * *
Where * * * * * * * * Where-Object
Microsoft.PowerShell.Utility
Name Alias Clixml Command Csv Culture Custom Date Debug Default
—- —– —— ——- — ——- —— —- —– ——-
Add * * * * * * * * *
Clear * * * * * * * * *
Compare * * * * * * * * *
ConvertFrom * * * ConvertFrom-Csv * * * * *
ConvertTo * * * ConvertTo-Csv * * * * *
Disable * * * * * * * * *
Enable * * * * * * * * *
Export Export-Alias Export-Clixml * Export-Csv * * * * *
Format * * * * * Format-Custom * * *
Get Get-Alias * * * Get-Culture * Get-Date * *
Group * * * * * * * * *
Import Import-Alias Import-Clixml * Import-Csv * * * * *
Invoke * * * * * * * * *
Measure * * Measure-Command * * * * * *
New New-Alias * * * * * * * *
Out * * * * * * * * Out-Default
Read * * * * * * * * *
Register * * * * * * * * *
Remove * * * * * * * * *
Select * * * * * * * * *
Send * * * * * * * * *
Set Set-Alias * * * * * Set-Date * *
Sort * * * * * * * * *
Start * * * * * * * * *
Tee * * * * * * * * *
Trace * * Trace-Command * * * * * *
Unregister * * * * * * * * *
Update * * * * * * * * *
Wait * * * * * * * * *
Write * * * * * * * Write-Debug *
Microsoft.PowerShell.Host
Name Transcript
—- ———-
Start Start-Transcript
Stop Stop-Transcript
Microsoft.PowerShell.Management
WARNING: column “EventLog” does not fit into the display and was removed.
Name ChildItem Computer ComputerMachinePassword ComputerRestore ComputerRestorePoint ComputerSecureChannel Connection Content
—- ——— ——– ———————– ————— ——————– ——————— ———- ——-
Add * Add-Computer * * * * * Add-…
Checkpoint * Checkpoint-Computer * * * * * *
Clear * * * * * * * Clea…
Complete * * * * * * * *
Convert * * * * * * * *
Copy * * * * * * * *
Debug * * * * * * * *
Disable * * * Disable-ComputerRestore * * * *
Enable * * * Enable-ComputerRestore * * * *
Get Get-ChildItem * * * Get-ComputerRestorePoint * * Get-…
Invoke * * * * * * * *
Join * * * * * * * *
Limit * * * * * * * *
Move * * * * * * * *
New * * * * * * * *
Pop * * * * * * * *
Push * * * * * * * *
Register * * * * * * * *
Remove * Remove-Computer * * * * * *
Rename * Rename-Computer * * * * * *
Reset * * Reset-ComputerMachinePassword * * * * *
Resolve * * * * * * * *
Restart * Restart-Computer * * * * * *
Restore * Restore-Computer * * * * * *
Resume * * * * * * * *
Set * * * * * * * Set-…
Show * * * * * * * *
Split * * * * * * * *
Start * * * * * * * *
Stop * Stop-Computer * * * * * *
Suspend * * * * * * * *
Test * * * * * Test-ComputerSecureChannel Test-Connection *
Undo * * * * * * * *
Use * * * * * * * *
Wait * * * * * * * *
Write * * * * * * * *
Microsoft.PowerShell.Security
Name Acl AuthenticodeSignature Credential ExecutionPolicy PfxCertificate SecureString
—- — ——————— ———- ————— ————– ————
ConvertFrom * * * * * ConvertFrom-SecureString
ConvertTo * * * * * ConvertTo-SecureString
Get Get-Acl Get-AuthenticodeSignature Get-Credential Get-ExecutionPolicy Get-PfxCertificate *
Set Set-Acl Set-AuthenticodeSignature * Set-ExecutionPolicy * *
Here is the code for CONVERTTO-XXX.PS1
#requires -version 2.0
param(
[Parameter(ParameterSetName=”xxx”, Mandatory=$true, Position=0)]
$InputObject,
[Parameter(ParameterSetName=”xxx”, Mandatory=$true)]
$yProperty,
[Parameter(ParameterSetName=”xxx”, Mandatory=$true)]
$xProperty,
[Parameter(ParameterSetName=”xxx”)]
[ScriptBlock]
$CellValue = {”TRUE”},
[Parameter(ParameterSetName=”xxx”)]
$NullValue = “-”,
[Parameter(ParameterSetName=”TEST”, Mandatory=$true)]
[Switch]
$Test
)
function Assert
{
param(
[Parameter(Mandatory=$true, Position=0)]
[ScriptBlock]$script,
[Parameter(Position=1)]
$Message=$null
)
if (!(&$script))
{
$ThrowAway,$stack = Get-PscallStack
$msg = @”
ASSERT( $script ) FAILED at
$($stack | out-string)
$Message
Continue script execution?
“@
if ( !($pscmdlet.ShouldContinue($msg, “ASSERT FAILED”)))
{
exit
}
}
}
function Add-Property
{
param(
[Parameter(Mandatory=$true, Position=0)]
[ValidateNotNullOrEmpty()]
$obj,
[Parameter(Mandatory=$true, Position=1)]
[ValidateNotNullOrEmpty()]
[String]
$prop,
[Parameter(Mandatory=$true, Position=2)]
$value)
Add-Member -InputObject $obj -MemberType NoteProperty -Name $prop -Value $value -Force
}
if ($PSCmdlet.ParameterSetName -eq “TEST”)
{
foreach ($snapin in Get-PSSnapin) {
Write-output $snapin.Name
$InputObject = get-command -PSSnapin $snapin -type Cmdlet |sort noun
&$MyInvocation.MyCommand.Path $InputObject -nullValue “*” -YProperty Verb -xProperty Noun -CellValue {$args[0].name}|ft -auto |out-string –stream
}
exit
}
foreach ($group in $InputObject | Group-Object -Property $yProperty |Sort Name)
{
$outObj = New-Object psobject
Add-Property $outObj Name $group.Name
foreach ($inObj in $InputObject)
{
$Xname = $inObj.$xProperty
Assert {$Xname } “Property [$XProperty] not found on input object”
Assert {$Xname -ne “NAME” } “Cannot have ‘Name’ as a property”
if ($InObj.$yProperty -eq $group.Name)
{
Add-Property $outObj $Xname (&$CellValue $inObj)
}elseif (!$outObj.$XName)
{
Add-Property $outObj $Xname $nullValue
}
}
Write-Output $outObj
}
[11/23 update – Thanks to Richard Siddaway for pointing out that the original version of this script required the script to be in a PATH directory. I modified the script to fix that. If you are a script, this is how you call yourself so you work whether or not your in the same directory or in a PATH directory
&$MyInvocation.MyCommand.Path
]
Enjoy!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Jonathan Medd (of the Get-Scripting Podcast) held a PowerShell Panel discussion at TechEd EMEA where he asked various members of the PowerShell community questions. I had a blast participating in this. The group consisted of:
- Jonathan Medd – Get-Scripting Podcaster and winner of the Quest PowerPack contest
- Ben Pearce – aka Gesticulating Ben – aka Martin Luther Pearce - Microsoft Permier Field Engineer, blogger, great WMI explainer and co-presenter of some of my talks.
- Richard Siddaway - Powershell MVP. blogger and founder of the world´s first PowerShell Users Group.
- Dmitry Sotnikov - Powershell MVP, blogger and creator of PowerGUI
- James O’Neill – Microsoft, blogger and creator of the PowerShell Management Library for Hyper-V
- Tobias Weltner - Powershell MVP, author, creator of PowerShell Plus.
- Jeffrey Snover - (me)
- Marc van Orsouw – aka Mow, aka “The PowerShell Guy” Powershell MVP and provider of the wonderful PowerTab tab completion.
We wanted to have more people on stage but we ran of chairs!
Rolf Masuch the leader of the German PowerShell usergroup was there and took this picture so now you can see the faces behind the names.

Cheers!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
A number of you have asked when Microsoft would be providing training for Powershell. Apparently the answer is “NOW”. I just came across this 3 day instructor led training class, Course 6434 “Automating Windows Server 2008 Administration with Windows PowerShell”.
I don´t know anything about the course so if any of you have taken it, I´d love to hear what you think.
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Attached are the slides I presented at the PDC.
Enjoy!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
icomasoft has just released VI PowerScripter. PowerScripter is a PowerShell based extension module to VMWare’s VI Client. I have not seen it but looking at the online manual has got to make any admin start drooling. The thing I like about this so much is that it leverages both PowerGadgets and PowerGUI. This allows them to deliver a great experience to their customers without having to code it all themselves. As those products get better, their product gets better. Happy happy all around.
I haven’t used it (since I don’t have a big VMWare environment [surprise :-) ]) so I’d love to hear from anyone that has used it.
Correct me if I’ve got this wrong but it seems like PowerShell has enabled a arm’s race between VMWare and System Center Virtual Machine Manager (the other week there was a post that showed how to do Quick Migrate a VMware virtual machine from one ESX server to the other Quick Migration for VMware - The Power of PowerShell). Each of them is able to deliver additional great functions to their community without having to wait a year for the next product release cycle. I think this is absolutely great for customers and highlights one of the key advantages of PowerShell - once you’ve instrumented your product with PowerShell, it enables you to be very agile in the marketplace. That is a clear and present business advantage.
Cheers!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Scott Fulton has a good article describing my PDC talk HERE. In it he says, “Starting with version 2, a user can conceivably write new PowerShell cmdlets in PowerShell, using the built-in IDE, completely independently of Visual Studio.” This is true but there is more to the story so here it is.
PowerShell’s Integrated Scripting Environment (ISE) actually uses an editor control which uses the same technology that will be in the next version of Visual Studio. It is a great control and get’s better and better with each snapshot we take. We need to stop taking snapshots long before VS because we’ll ship in W7/WS08R2 so the functions will not be identical. They obviously have few more devs working on it than we do so their use of the control will be much more comprehensive than ours will as well. Still - it is pretty great technology. The Pre Beta users should see signifcant advances from our CTP2 release.
Cheers!
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
In my previous blog post, I mentioned that we were all very proud and optimistic about W7 and WS08R2. There are tons of reasons for this including how well the project has been run, the hardcore focus on quality and performance, the great new features, etc. Another reason we are so optimistic is the availability of new cmdlets. There are so many teams doing so much good work with PowerShell that it would spin your head. Unfortunately - it is there news to tell not ours so we can have to bite our tongues and can’t say anything. We’ll know that the Pre-Beta is out, we had Dan Harman do a quick look-see at WS08R2 for cmdlet coverage. What he did was to try and install as many roles/features as he could and then count the cmdlets.
You need to understand the limitations of the information I’m about to give you. First - Dan used last week’s build so they numbers may vary from what you see in the Prebeta. Next, Dan installed as many roles as he could do easily but some of the roles require advanced configuration etc that he didn’t have time to pursue so we are reporting on the easy to install subset. Next this ONLY reports CMDLET - it does not take into account Providers. Also a number of these cmdlets are in fact tools which you feed data to so they actually do a ton of work but get counted as a single cmdlet (e.g. DiagPack and BestPractices cmdlets). With all those caveats, here is what Dan found:
Roles installed: 13/16
Role services installed: 94/101
Features installed: 40/40
CORE POWERSHELL
· Microsoft.PowerShell.Core 37 Cmdlets
· Microsoft.PowerShell.Utility 87 Cmdlets
· Microsoft.PowerShell.Host 2 Cmdlets
· Microsoft.PowerShell.Management 80 Cmdlets
· Microsoft.PowerShell.Security 10 Cmdlets
· Total: 216
ADDITIONAL
· Microsoft.PowerShell.Diagnostics 4 Cmdlets
· Microsoft.WSMan.Management 13 Cmdlets
· ADRMS.PS.Admin 15 Cmdlets
· Microsoft.Windows.ServerManager 3 Cmdlets
· Microsoft.Windows.ServerManager.Migration 5 Cmdlets
· Windows.ServerBackup 30 Cmdlets
· activedirectory 76 Cmdlets
· BestPractices 4 Cmdlets
· DiagPack 2 Cmdlets
· FailoverClusters 66 Cmdlets
· FileTransfer 8 Cmdlets
· GroupPolicy 25 Cmdlets
· NetworkLoadBalancingClusters 35 Cmdlets
· PSDiagnostics 10 Cmdlets
· RemoteDesktopServices 0 Cmdlets (?? – I think they´re just doing a provider)
· WebAdministration 62 Cmdlets
· Total: 358
Take that and add in the fact that W7 & WS08R2 will have PowerShell V2 which gives you mind-boggling set of new features including remoting and the ability for the community to write their own cmdlets using Powershell and PowerShell_ISE and I think it should be pretty obvious why we are so proud and optimistic about the upcoming Windows releases.
You should definately be planning to beta test these releases. I think you will decide that you’ll want to adopt them sooner than later. You’ll figure out what’s best for your circumstances but I think that what you’ll conclude.
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Wow - the NY Times is Johnny-on-the-spot with their analysis of the 10 best features of Windows 7 for IT Pros HERE. It turns out that PowerShell is responsible for 3 of them! Come review time, I’m telling my boss that we’re responsible for 30% of the value of W7. :-) !!! Just to be clear - I’m totally joking. Windows 7 is an awesome release. It has a ton of great stuff in it. PowerShell IS awesome but it is a tiny just one of the huge wave of improvements. You’ll decided for yourself but we are all extremely proud and optimistic about W7.
Check it out:
Windows Troubleshooting Platform
The Windows Troubleshooting Platform is a new, comprehensive approach to solving end user problems via troubleshooting packs, which address problems and can be applied to PCs throughout the environment to rectify those issues; and the Windows Troubleshooting Toolkit, which allows you as the administrator to create your own troubleshooting packs when you identify specific issues within your own infrastructure. Also, but separately, a new Problem Steps Recorder allows an end user to record the steps he takes leading up to a problem, capture those steps into automatically created screen grabs, and e-mail that to an administrator or help desk representative for easier problem resolution.
Windows PowerShell Integrated Scripting Environment
With PowerShell’s popularity, Microsoft has introduced into Windows 7 a graphical interface for PowerShell that makes it very easy to learn the scripting language and use it in a color-coded, easy-to-read environment. Developing, debugging and running the scripts in this new environment is much easier than the previous single-command-prompt method.
PowerShell Remoting
Also new to PowerShell is support for the WS-Management protocol that allows you to remotely run commands on client PCs. You can use this capability on a one-to-one basis, say for specific requests in response to help desk calls, or you can fan out with one-to-many remoting and run cmdlets on multiple PCs from within the Windows PowerShell Integrated Scripting Environment.
The reason why I’m counting Troubleshooting is because it is all PowerShell based! (I’m actually listening to their talk at the PDC right now - it’s so cool!). That team had their own design for diagnostics and when they stepped back and looked at it they concluded that at the end of the day, they were going to live or die based upon CONTENT. They looked at the effort involved with getting that content and then looked at the power and momentum that PowerShell had and decided to take a dependency on us. Great stuff!
Thanks to Narayanan Lakshmanan for pointing this article out.
Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Windows PowerShell Blog: