The Scripting Games have started and I am pleased to see that a few teams have already published interesting approaches to the first test event. Nonetheless I see that some people still persevere with some of the common mistakes beginners do.
In this blog post, I want to shed some light on two of these common mistakes.
The first one it’s not really a mistake but a bad habit. Let's see why. In the first test event you are asked to perform some quite complex computer inventory tasks. As you know, inventorying Windows-based computers generally passes through WMI queries through the use of the Get-WmiObject cmdlet:
Get-WmiObject–class win32_service –computername (Get-Content serverlist.txt)
Some people naïvely tend to remove returned objects from the pipeline using Where-Object cmdlet.
But, and this is very important, when you are retrieving many information from many distant servers, and piping the resulting objects to Where-Object, you could encounter performance problems due to the huge amount of data your station has to analyze and keep or discard.
The tip here (that Don Jones named ‘Filter Left’ in his training) is to make use of the –Filter parameter, which is available for many Powershell cmdlets:
Get-Command -ParameterName filter CommandType Name ----------- ---- Cmdlet Add-Content Cmdlet Clear-Content Cmdlet Clear-Item Cmdlet Clear-ItemProperty Cmdlet Copy-Item Cmdlet Copy-ItemProperty Cmdlet Get-Acl Cmdlet Get-ChildItem Cmdlet Get-Content Cmdlet Get-Item Cmdlet Get-ItemProperty Cmdlet Get-Job Cmdlet Get-WmiObject Cmdlet Get-WSManInstance Cmdlet Invoke-Item Cmdlet Move-Item Cmdlet Move-ItemProperty Cmdlet New-ItemProperty Cmdlet Remove-Item Cmdlet Remove-ItemProperty Cmdlet Remove-Job Cmdlet Rename-ItemProperty Cmdlet Resume-Job Cmdlet Set-Acl Cmdlet Set-Content Cmdlet Set-Item Cmdlet Set-ItemProperty Cmdlet Stop-Job Cmdlet Suspend-Job Cmdlet Test-Path Cmdlet Wait-Job
Using this –Filter parameters boosts your WMI query performance by letting the WMI service itself on the remote server do the homework of filtering information.
A quick script allows us to verify this statement, even on local queries:
$FilterDuration = (Measure-Command -Expression {Get-ChildItem -Path $env:windir\system32 -Filter *.dll}).Milliseconds $WhereDuration = (Measure-Command -Expression { Get-ChildItem -Path $env:windir\system32 | Where-Object Extension -eq ".dll"}).Milliseconds "Filtering with -Filter: $FilterDuration ms" "Filtering with Where-Object: $WhereDuration ms" Filtering with -Filter: 239 ms Filtering with Where-Object: 803 ms
There you go: unsurprisingly we get 200 milliseconds for –Filter versus more than 800 milliseconds for Where-Object.
Let me move on to the second mistake, which is not knowing that you have to explicitly set the value of the parameter -ErrorAction to Stop in your WMI queries if you want to trap errors using the Try {} construct.
This is because some exceptions returned by WMI queries aren't terminating errors.
For example, the following script returns a big red error message, like if the Catch {} block was skipped:
Try {
get-wmiobject -class "Win32_PhysicalMemory" -computername Ghostserver
}
Catch {
"WMI query failed..."
}
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
While the following one properly catches the error and shows the predefined error message:
Try { get-wmiobject -class "Win32_PhysicalMemory" -computername Ghostserver -ErrorAction Stop } catch { "WMI query failed..." }
The major advantage of transforming your WMI errors in terminating exceptions and writing to the host nicely composed error messages is that you show that you master your code and that your scripts is designed well enough to cope with unforeseen issues.
Hope this helps.
No comments:
Post a Comment