As we said, off the top of our heads, there is no easy way to do this, but it just bothered me to the degree that I could not let it go.
All that being said. I went back to my personal library of old file system code, and completely refactored an item from the past and got this thing to work for the look and feel you are after.
It took several hours of experimentation to figure this out BTW, to dig thru my library, and find a suitable item to mess with. Yet, once I got it, is was an OMG moment and this reminded my why I never throw my code away, that I thought I’d not need again.
The code, will create the report, populate it out to a pre-created CSV dynamically, once complete, it will then show it on screen, in Out-GridView as it will show in Excel. Based on your use case, no further manipulation would be required. All with no custom object required.
So, is this use case doable, yes, and I just proved that. Was it easy, nope. Yet, most new attempt at some things are a challenge, in many cases and that was why I decided to tackle it. Just because. Once done though, just like when you see an amazing magic trick, once they show you how it’s done, it’s often dead simple.
Yet in PowerShell, there is always a dozen different ways to do something, and I am sure others could make this more elegant. so, this is just my success at it. Take it for what it is worth. It took a while to whittle this all down to the smallest amount of code I could wire up.
### Nested tabular reporting
Clear-Host
# Remove old log and create Log
Remove-Item -Path 'D:\Scripts\Log.csv' -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
$Output = 'D:\Scripts\Log.csv'
Add-Content $Output 'RootObject,DataKey,ChildObjects'
# Populate dataset for each object - Change the input to suit your needs
$DataSetArray_1 = Get-Process |
Sort-Object -Property ProcessName |
Select-Object -Property ProcessName -Unique
$DataSetArray_2 = Get-Process |
Sort-Object -Property ProcessName |
Select ProcessName, Id, SI
# Retrieve the dataset from each Object path
ForEach ($DataSet_1 in $DataSetArray_1)
{
# "Checking for matches for $($DataSet_1.ProcessName)"
$DataCheck = 0
# Write-Host "Getting $($DataSet_1.ProcessName)" -Foreground Gray
ForEach ($DataSet_2 in $DataSetArray_2)
{
# Write-Host "Validating against $($DataSet_2.ProcessName)" -Foreground Green
#Compare DataSet
If ($DataSet_1.ProcessName -eq $DataSet_2.ProcessName)
{
#Output results to csv
If ($DataCheck -eq 0)
{
$DataCheck = 1
$Text = "$($DataSet_1.ProcessName), $($DataSet_2.Id), $($DataSet_2.ProcessName)"
# Write-Host "Adding primary results content $Text" -Foreground Cyan
Add-Content $Output $Text
}
else
{
$Text = ", $($DataSet_2.Id), $($DataSet_2.ProcessName)"
# Write-Host "Adding secondary results content $Text" -Foreground Yellow
Add-Content $Output $Text
}
}
}
}
Import-Csv -Path 'D:\Scripts\Log.csv' # Send to screen
Import-Csv -Path 'D:\Scripts\Log.csv' |
Out-GridView -Title 'Nested processes report' # Send to GUI
Start-Process -FilePath Excel 'D:\Scripts\Log.csv' # Open in Excel
# Results (truncated for this response)
RootObject DataKey ChildObjects
---------- ------- ------------
...
browser_broker 7796 browser_broker
BuildService 3904 BuildService
conhost 7248 conhost
12800 conhost
5256 conhost
CoordService 4700 CoordService
csrss 880 csrss
38024 csrss
ctfmon 8008 ctfmon
dasHost 5052 dasHost
...