I'm working on a new feature for one of my modules that requires me to know what kind of files I am working with. It's quite easy in PowerShell, and without a lot of code, you can reasonably quickly get necessary information about data stored on your desktop or anywhere else for that matter.
Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force
Or if you want to see all available properties for an object you can display it this way.
Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Select-Object -First 1 | Format-List *
This, of course, is not all available data. If you right-click on any file and switch tab to Details, you will see a lot of special info is available. What's unique about it – is that each file type will have different kinds of information.
As you can see above I've quickly checked Excel file, MSI file, an EXE file, all having similar but not the same information.
Now that you know what I'm after, let me show you what I've done. With this small little function below, I can quickly get all the required information in no time. How do I use it? You can use it either via pipeline from Get-ChildItem or by providing full path inputs to it.
# Option 1 Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Get-FileMetaData -Signature | Out-HtmlView -ScrollX -Filtering -AllProperties # Option 2 $Files = "$Env:USERPROFILE\Desktop\LAPS.x64.msi", "$Env:USERPROFILE\Desktop\DigiCertUtil.exe" $Files | Get-FileMetaData -Signature | Out-HtmlView -ScrollX -Filtering -AllProperties # Option 3 Get-FileMetaData -File $Files | Out-HtmlView -ScrollX -Filtering -AllProperties # Option 4 Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Where-Object { $_.Attributes -like '*Hidden*' } | Get-FileMetaData -Signature | Out-HtmlView -ScrollX -Filtering -AllProperties
This function has two parameters (for now) – one taking [System.IO.DirectoryInfo] (basically Get-ChildItem output) or [string], the other one is a switch deciding whether you want to check if the file is signed or not.
The output you see above is only output from two files. I'm specifically using the PSWriteHTML Out-HtmlView command with the ScrollX parameter to make sure it shows everything that there is to show. Additionally, please notice I'm using AllProperties switch. This switch is special when it comes to Out-HtmlView. This is because most of the commands such as Format-Table or Export-CSV or even Out-HtmlView, by default, display properties only from 1st entry in the array. It's efficient and fast. However, in the case of Get-FileMetaData, where each file has different properties it doesn't make much sense. Using AllProperties does a “prescan” of an Array, and then any property missing from 1st element is added so that Out-HtmlView can display everything there is to show. Of course, if you're working with the same file types that don't differ from each other, you can skip that parameter or Out-HtmlView for that matter.
If you just want to peek into an object quickly, you can use Format-List * to see all parameters without using Out-HtmlView.
$Files = "$Env:USERPROFILE\Desktop\LAPS.x64.msi", "$Env:USERPROFILE\Desktop\DigiCertUtil.exe" $Files | Get-FileMetaData -Signature | Format-List
If you intend to use Out-HtmlView for this, please make sure to install it first. You can do so using Install-Module
Install-Module PSWriteHTML -Force
Ok, so now that you know what you're getting into, there are two ways to get Get-FileMetaData function. Copy and paste from the source below or install one of my “do it all” modules called PSSharedGoods. I use the PSSharedGoods module as my glue module where multiple other modules use it as a dependency, or I copy the functions from it during build time and convert them to private services within other modules to minimize dependencies.
Install-Module PSSharedGoods -Force
Keep in mind that I update my modules quite often, fixing bugs, so always up to date versions of said modules can be downloaded from PowerShellGallery using Install-Module, as mentioned above. When I push my modules into PowerShellGallery, I minimize them and optimize them for production use. If you're into development and want to work in a more structured way where each function has separate files, multiple folders, and so on, I would highly recommend visiting my GitHub pages for PSWriteHTML or PSSharedGoods.
Here's the function in all it's glory for you to copy/paste and use as you see fit.
function Get-FileMetaData { <# .SYNOPSIS Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .DESCRIPTION Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .PARAMETER File FileName or FileObject .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Where-Object { $_.Attributes -like '*Hidden*' } | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .NOTES #> [CmdletBinding()] param ( [Parameter(Position = 0, ValueFromPipeline)][Object] $File, [switch] $Signature ) Process { foreach ($F in $File) { $MetaDataObject = [ordered] @{} if ($F -is [string]) { $FileInformation = Get-ItemProperty -Path $F } elseif ($F -is [System.IO.DirectoryInfo]) { #Write-Warning "Get-FileMetaData - Directories are not supported. Skipping $F." continue } elseif ($F -is [System.IO.FileInfo]) { $FileInformation = $F } else { Write-Warning "Get-FileMetaData - Only files are supported. Skipping $F." continue } $ShellApplication = New-Object -ComObject Shell.Application $ShellFolder = $ShellApplication.Namespace($FileInformation.Directory.FullName) $ShellFile = $ShellFolder.ParseName($FileInformation.Name) $MetaDataProperties = [ordered] @{} 0..400 | ForEach-Object -Process { $DataValue = $ShellFolder.GetDetailsOf($null, $_) $PropertyValue = (Get-Culture).TextInfo.ToTitleCase($DataValue.Trim()).Replace(' ', '') if ($PropertyValue -ne '') { $MetaDataProperties["$_"] = $PropertyValue } } foreach ($Key in $MetaDataProperties.Keys) { $Property = $MetaDataProperties[$Key] $Value = $ShellFolder.GetDetailsOf($ShellFile, [int] $Key) if ($Property -in 'Attributes', 'Folder', 'Type', 'SpaceFree', 'TotalSize', 'SpaceUsed') { continue } If (($null -ne $Value) -and ($Value -ne '')) { $MetaDataObject["$Property"] = $Value } } if ($FileInformation.VersionInfo) { $SplitInfo = ([string] $FileInformation.VersionInfo).Split([char]13) foreach ($Item in $SplitInfo) { $Property = $Item.Split(":").Trim() if ($Property[0] -and $Property[1] -ne '') { $MetaDataObject["$($Property[0])"] = $Property[1] } } } $MetaDataObject["Attributes"] = $FileInformation.Attributes $MetaDataObject['IsReadOnly'] = $FileInformation.IsReadOnly $MetaDataObject['IsHidden'] = $FileInformation.Attributes -like '*Hidden*' $MetaDataObject['IsSystem'] = $FileInformation.Attributes -like '*System*' if ($Signature) { $DigitalSignature = Get-AuthenticodeSignature -FilePath $FileInformation.Fullname $MetaDataObject['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject $MetaDataObject['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer $MetaDataObject['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber $MetaDataObject['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore $MetaDataObject['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter $MetaDataObject['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint $MetaDataObject['SignatureStatus'] = $DigitalSignature.Status $MetaDataObject['IsOSBinary'] = $DigitalSignature.IsOSBinary } [PSCustomObject] $MetaDataObject } } }