I'm always looking for ways to do more with PowerShell. And often, once I find a technique, I look for other areas where I can apply it. I'm hoping that today might be like that for you. You may not have a need to duplicate my work in this article, but hopefully you'll recognize value in the techniques and concepts and find your own ways to apply them. What I am interested in is extending and improving PowerShell results.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
In PowerShell, there are different types of commands. One type is an ExternalScript. These are PowerShell script files that can be run without specifying the path.
Of course, there is more to the output.In short, this is just another file. As such I can get information.
Get-Command profile.ps1 | Select-Object -property Name,Path,@{Name="LastWriteTime";Expression={(Get-Item $_.path).lastWriteTime}}, @{Name="Location";Expression={Split-Path $_.source}}
It is important that you follow the object in the pipeline. Get-Command is writing a System.Management.Automation.ExternalScriptInfo object to the pipeline. I already know some of the properties I can use. But I am also using a hashtable to define new properties. I'm creating a property called LastWriteTime. The value is the LastWriteTime value of the file object which I'm getting with Get-Item. I'm also creating a property called Location which will be the script's parent directory.
It is just as easy now to apply this to all external scripts.
Get-Command -CommandType ExternalScript |Select-Object -property Name,Path, @{Name="LastWriteTime";Expression={(Get-Item $_.path).lastWriteTime}}, @{Name="Location";Expression={Split-Path $_.source}} | Sort-Object -Property Location | Format-Table -GroupBy Location -property Name,LastWriteTime
Now for the fun part. I want this type of information all the time. But I don't want to have to type out the custom hashtables. The solution is to add custom type extensions. And I can make this very easy.
I'm going to use a command from my PSTypeExtensionToolsmodule which you can install from the PowerShell Gallery. It works in Windows PowerShell and PowerShell 7, even cross-platform. Here's my code.
$tname = "System.Management.Automation.ExternalScriptInfo" Add-PSTypeExtension -TypeName $tname -MemberType ScriptProperty -MemberName Location -Value {Split-Path $this.source} Add-PSTypeExtension -TypeName $tname -MemberType ScriptProperty -MemberName Target -Value { $f = Get-Item $this.source #Use the target if the file is linked if ($f.Target) { $f.target } else { #use the full name $f.fullname } } Add-PSTypeExtension -TypeName $tname -MemberType ScriptProperty -MemberName LastWriteTime -Value { $f = Get-Item $this.source if ($f.Target) { (Get-Item -path $f.target).LastWriteTime } else { $f.LastWriteTime } }
Yes, you can do something similar with Add-Member. But now see how easy my PowerShell command is.
On my system, I have a few scripts with symlinks which is why I'm showing a Target property.
These changes are only good in this PowerShell session. If I want them all the time, I'd have to insert the code into a profile script. However, I can simplify that because I can export my extensions to a json file.
Get-PSTypeExtension -TypeName $tname | Export-PSTypeExtension -Path c:\scripts\externalscriptinfo.json
Then in my profile, I can have a single line of code to bring them in.
Import-PSTypeExtension C:\scripts\externalscriptinfo.json
But wait...there's more! Remember that awkward command from earlier to format external scripts as a grouped table? I can simplify that as well.
If you don't have it, install the PSScriptTools module from the PowerShell Gallery. Then you can use the New-PSFormatXML command to create a custom formatting file.
Get-Command mybackuppending.ps1 | New-PSFormatXML -Path c:\scripts\externalscriptinfo.format.ps1xml -Typename $tname -GroupBy Location -Properties Name, Target, LastWriteTime -ViewName location -FormatType table
In my PowerShell profile script I will import this file.
Update-FormatData C:\scripts\externalscriptinfo.format.ps1xml
Look how easy this is now.
I don't want to overwrite PowerShell's default view, but I can add my own. I love making PowerShell do what I need it to do. With just a little extra work I now have the information I need. In fact, I can already think of a few additions such as getting the file size and a new format view.
I hope this inspires you to transform PowerShell to make your life and work easier. Good luck!