I am always stressing that PowerShell is all about the objects. If you keep this in mind, PowerShell is pretty easy to use. Get a bunch of things, and select the details that you want to see or work with. Out of the box PowerShell gives you some very rich objects to work with from simple files to Active Directory users. What I like even more is that you can create your own properties "on-the-fly" to meet your needs. It is almost like magic. You can create new properties practically out of thin air. But sometimes even this process can get a bit tedious or overwhelming. Let me offer some solutions.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Let's begin a PowerShell expression you might run to discover what system drivers are currently running.
Get-CimInstance -class win32_systemdriver -filter "state='running'" | Select-Object Name,Description,ServiceType,StartMode,@{Name="Path";Expression={$_.pathname}}, @{Name="Company";Expression = {(get-item $_.pathname).versioninfo.CompanyName}}, @{Name="Product";Expression = {(get-item $_.pathname).versioninfo.ProductName}}, @{Name="Version";Expression = {(get-item $_.pathname).versioninfo.productversion}}, @{Name="Updated";Expression = {(get-item $_.pathname).lastwritetime}}, @{Name="Computername";Expression={$_.SystemName}}
Effective, although, this is a lot of typing. With Select-Object I created a few alias properties using hashtables. For example, instead of using 'SystemName,' I wanted to display 'Computername'. I also wanted to include information that wasn't a part of the Win32_SystemDriver object. The object did include a property for the file path, so I create a few custom properties that retrieved version information from the file.
@{Name="Company";Expression = {(get-item $_.pathname).versioninfo.CompanyName}}, @{Name="Product";Expression = {(get-item $_.pathname).versioninfo.ProductName}}, @{Name="Version";Expression = {(get-item $_.pathname).versioninfo.productversion}}
The challenge is that if I want to re-run the command perhaps with a different filter or a different subset of properties, I have to re-type a lot of code. I'm betting you don't like to type. Let's look at some options.
First, you could save your custom property definitions as variables.
$path = @{Name="Path";Expression={$_.pathname}} $company = @{Name="Company";Expression = {(get-item $_.pathname).versioninfo.CompanyName}}
I can use these variables in my Select-Object statement.
Get-CimInstance -class win32_systemdriver -filter "state='running'" | Select-Object Name,Description,StartMode,$path,$company | Group Company
As long as I have these variables defined I can use them as often as I want.
But perhaps the best approach is to create you own type extension. Before you run away thinking I'm asking you to create an arcane looking xml file, this is actually quite easy. First, you need typename of the object you want to extend. You see this every time you use Get-Member.
I'm going to save this value with a line of code.
$type = get-ciminstance win32_systemdriver | select-object -first 1 | Get-member | Select-object -ExpandProperty TypeName
More than likely this variable will be an array of names so I'll use $type[0]
Next, I'm going to use the Update-TypeData cmdlet. Yes, you can use a ps1xml file but you don't have to. I can just easily create my alias properties.
Update-TypeData -TypeName $type[0] -MemberType AliasProperty -MemberName Path -Value pathname -force Update-TypeData -TypeName $type[0] -MemberType AliasProperty -MemberName Computername -Value SystemName -force
The Value is the name of the actual property. I'm using –Force to overwrite any existing property definitions. I can also create ScriptProperties which will run a small bit of code to get a value.
Update-TypeData -TypeName $type[0] -MemberType ScriptProperty -MemberName Company -Value {(get-item $this.pathname).versioninfo.CompanyName} -force Update-TypeData -TypeName $type[0] -MemberType ScriptProperty -MemberName "Product" -Value {(get-item $this.pathname).versioninfo.ProductName} -force Update-TypeData -TypeName $type[0] -MemberType ScriptProperty -MemberName "Version" -Value {(get-item $this.pathname).versioninfo.productversion} -force Update-TypeData -TypeName $type[0] -MemberType ScriptProperty -MemberName "Updated" -Value {(get-item $this.pathname).lastwritetime} -force
The Value is the scriptblock portion of the Expression key I defined early. The only other change you must remember to make is to use $this instead $_.
Now, these properties are defined for any Win32_SystemDriver object.
Get-CimInstance Win32_SystemDriver -filter "state='running'" | Select-Object Name,Description,ServiceType,StartMode,Path,Company,Product,Version,Updated,Computername | Out-Gridview -title "Running Drivers"
See how much easier and cleaner that is? I can use these properties as much as I want.
Get-CimInstance Win32_SystemDriver -filter "servicetype='kernel driver'" | Where Company -notmatch "microsoft" | Sort StartMode,Name | Format-Table -GroupBy StartMode -Property State,Name,Company
However, remember that these are *your* properties and not part of WMI which is why I can't use them in the Get-CimInstance filter.
A few final caveats on this technique. These extensions will only last for as long as my PowerShell session is running. If I always want to have them, I would put this code in my PowerShell profile script. You should also be careful when writing scripts or modules that use these extensions. If someone is running your code and they haven't added the type extensions the code won't work. In a module, you can add the Update-TypeData lines into your .psm1 file. Or you can take the extra steps to create an actual .ps1xml file with the extensions and include that with your module.
Give my code samples a spin and let me know what you think.
Indeed very cool stuff, more powerful and simpler than Select-Object with an expression. thank you!