Automating the world one-liner at a time…
Let me start of by suggesting you take a few minutes and read Laerte Junior’s excellent blog entry Exceptional PowerShell DBA Pt1 Orphaned Users. Laerte does a great job describing the scenario, the approach he took to solving it and then he included the scripts! Really goodstuff Laerte!
When I looked at Laerte’s code, I recognized one of my least favorite PowerShell V1isms:
$Object = New-Object PSObject $Object | add-member Noteproperty LineNumber $LineNumber $Object | add-member Noteproperty Date $TodayDate $Object | add-member Noteproperty ServerName $svr $Object | add-member Noteproperty DatabaseName $Database $Object | add-member Noteproperty UserName $user.name $Object | add-member Noteproperty CreateDate $CreateDate $Object | add-member Noteproperty DateLastModified $DateLastModified $Object | add-member Noteproperty AsymMetricKey $user.AsymMetricKey $Object | add-member Noteproperty DefaultSchema $user.DefaultSchema $Object | add-member Noteproperty HasDBAccess $user.HasDBAccess $Object | add-member Noteproperty ID $user.ID $Object | add-member Noteproperty LoginType $user.LoginType $Object | add-member Noteproperty Login $user.Login $Object | add-member Noteproperty Orphan ($user.Login -eq "")
Gosh that is a horrible looking thing. This is one of the things we fixed with PowerShell V2 so you take a few minutes to learn this so you never have to do that mess again. Let’s get some help:
PS> Get-Help New-Object -Parameter Property
-Property <hashtable> Sets property values and invokes methods of the new object. Enter a hash table in which the keys are the names of properties or methods and the values are property value s or method arguments. New-Object creates the object and sets each property value and invokes each method in the order that they appear in the hash table. If the new object is derived from the PSObject class, and you specify a property that does not exist on the o bject, New-Object adds the specified property to the object as a NoteProperty. If the object is not a PSObjec t, the command generates a non-terminating error. Required? false Position? named Default value Accept pipeline input? false Accept wildcard characters? false
Thats right – you can now specify a hashtable and we’ll set these as NoteProperties on an object. What that means is that you can now write code like this:
$Object = New-Object PSObject -Property @{ LineNumber = $LineNumber Date = $TodayDate ServerName = $svr DatabaseName = $Database UserName = $user.name CreateDate = $CreateDate DateLastModified = $DateLastModified AsymMetricKey = $user.AsymMetricKey DefaultSchema = $user.DefaultSchema HasDBAccess = $user.HasDBAccess ID = $user.ID LoginType = $user.LoginType Login = $user.Login Orphan = ($user.Login -eq "") }
or if you prefer:
$hash = @{ LineNumber = $LineNumber Date = $TodayDate ServerName = $svr DatabaseName = $Database UserName = $user.name CreateDate = $CreateDate DateLastModified = $DateLastModified AsymMetricKey = $user.AsymMetricKey DefaultSchema = $user.DefaultSchema HasDBAccess = $user.HasDBAccess ID = $user.ID LoginType = $user.LoginType Login = $user.Login Orphan = ($user.Login -eq "") } $Object = New-Object PSObject -Property $hash
This is SO much better than V1 – be sure to use it and to tell all your friends about it as well!
Experiment! Enjoy! Engage!
Jeffrey Snover [MSFT] Distinguished Engineer 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
Nice post Jeffrey! The simplifed syntax is great. But can you tell me why if I use the PowerShell V2 way my NoteProperties aren't sorted in the order I create them like they do using the V1 way?
Example:
$objectV1 = New-Object PSObject
$objectV1 | Add-Member NoteProperty FirstName MyFirstName
$objectV1 | Add-Member NoteProperty LastName MyLastName
$objectV1 | Add-Member NoteProperty Address MyAddress
$objectV1 | Add-Member NoteProperty City MyCity
$objectV1 | Add-Member NoteProperty State MyState
$objectV1 | Add-Member NoteProperty Zip MyZip
$objectV1
$objectV2 = New-Object PSObject -Property @{
FirstName = 'MyFirstName'
LastName = 'MyLastName'
Address = 'MyAddress'
City = 'MyCity'
State = 'MyState'
Zip = 'MyZip'
}
$objectV2
FirstName : MyFirstName
LastName : MyLastName
Address : MyAddress
City : MyCity
State : MyState
Zip : MyZip
That is the effect of using a hashtable. By definition, you cannot assume any ordering of the keys of a hashtable. For some reason they ended up in that order:
PS>$x= @{
______________________________________________________________________________________________________________
PS>$x.Keys
City
LastName
Address
State
FirstName
Zip
jps
Thank you very much. I'm actually a DBA who likes to powershell and not a powershell expert who likes to be DBA. It was the way I found to solve my problem to add the objects from various serevrs and one thing I always ask to powersehll community is to help me improve the code, optimize, and so I can understand more how I can make the necessary advantage of this fantastic shell.
Thanks for the tip!! I'm already changing my scripts!!
Thank you very much. I'm actually a DBA who likes to powershell and not a powershell expert who likes to be DBA. It was the way I found to solve my problem to add the objects for various servers and one thing I always ask to community is to help me improve the code, optimize, and so I can understand more how I can make the necessary advantage of this fantastic shell.
Thanks for the tip!! I'm already changing my scripts
Looks very nice, but doesn't help very much if you have to process text with name:value pairs from the messagage field of the security eventlog. I got about the same performance when I add the values one by one as noteproperty or when I collect them in a hash and add this in one step.
I posted the code using adding the hash as property at http://pauerschell.blogspot.com/2009/12/using-powershell-to-filter-eventlog.html
Hi Jeffrey,
I wonder why you didn't add release note which include changes since posh v1.0 on posh v2 RTM package.
When V2 RTM was released, one of the first things I did was replacing accelerators "$var = 1 | Select-Object Prop1, Prop2, ..." (well known and very effective trick) with modern native V2 approach "$var = New-Object -Property @{...}". And then, yes, I very quickly realized that in fact I often wanted properties to be in particular order, for various reasons. Thus, I had to restore old good tricks in approximately 50% of cases. This new feature is excellent, no doubts, but because of unpredictable property order it still does not cover what I expect from New-Object.
Would it be possible (for an end user or for Microsoft in a future version) to somehow apply an adapter - similar to the way array's are adapted for more flexible array use - that would honor the initial order of the hashtable's keys? Obviously it would be a breaking change to use a different implementation of IDictionary, but the unordered nature of keys in a hashtable has always made them slightly less useful in PowerShell IMHO.
I've started using PS just two weeks ago, and I am impressed. As far as scripting is involved, I was sooo much more productive, it's a delight.
But! Using PS *interactively*, as opposed to writing a batch file and executing it, is one very ugly experience. As commenting on the blog post probably is not the best communication channel, where is the proper place for me to whine and complain how bad is the PS command line interpreter?
Why is it that New-Object does not accept the most obvious (at least to me) for the TypeName Parameter namely a real Type?
e.g.
New-Object [System.Text.StringBuilder]
It's not a big deal. I'm just wondering...
@Stranger: I did some testing and found a work around.
New-Object ([System.Text.StringBuilder])
It appears to be a problem with the PowerShell parser.
@Jason - in strangers example he was passing a string "[System.Text.StringBuilder]" but in your example the parenthases causes PowerShell to evaluate an expression which produces a Type object. Type.ToString is then used to send the string parameter New-Object expects which becomes "System.Text.StringBuilder" so the two are not equivalent.
It may seem like Stranger is sending a Type object
to New-Object but in fact the parameter parser considers that a string. Anything in parenthases is evaluated as an expression. It's by design.
Very nice! When creating tens of thousands of custom objects, though, is this method (using a hash) the best for performance? If speed is more important than code elegance, what's the best way to create large numbers of custom objects with many (10+) new properties? Thanks!
Hey Rinimbi, regarding the performance of PSObjects, Karl Prosser, mow, and Brandon Shell have been batting this around for a while. Check out the following URL and in the first sentence he links to other posts that benchmark various methods of creating custom objects.
http://karlprosser.com/coder/2008/06/12/generating-a-propertybag-aka-pscustomobject-in-c/
But depending on your circumstances, I've found the best way to create a huge number of objects with uniform structure is to use the Add-Type cmdlet in PowerShell 2.0 to create an on-the-fly .NET class in C# syntax.
I've started using PS just two weeks ago, and I am impressed. I am so much more productive in terms of scripting, super. Really a big thank you for explaining the Real Time problem with the PowerShell parser.