The other day I was answering a question in the PowerShell Facebook group. This person was getting data from Active Directory and trying to organize the results in a way that met his business requirements. My suggestion was to use Group-Object and a custom grouping property. I am assuming you are familiar with Group-Object. You can specify a property name, and the incoming objects are grouped based on the property value. This is a handy way of organizing things into buckets. Sometimes you just want to group results, and other times you want to do something with each group. But if you read the full help and examples for Group-Object (you did, right?), you would see that there is a way to create a custom grouping. I thought I'd give you a few examples of how this works.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Custom Property Script Block
This conventional way to use Group-Object is to pipe one command to it and organize the incoming objects based on a property name.
Get-Childitem c:\scripts -file | Group-Object -Property Extension
You can try this example with any path on your computer to see how this works. However, PowerShell is magical, or at least flexible, and you can group on something you create out of thin air. You can use a script block and $_ to represent each object. This is the same concept in Select-Object to create custom properties. Although in this case, the Group-Object script block doesn't need to be named. Here's a simple example.
Get-ChildItem c:\scripts -file -Recurse -exclude *.exe |
Group-Object -property {$_.LastWriteTime.Year}
The files will be grouped based on the Year property of the LastWriteTime property.
As you can see, I've been doing this scripting thing for awhile. This example is simple and is easy to write in the console. But there's no limit to what you can put in the script block. Here's a more complex grouping and one that is analogous to what my friend was trying to do with Active Directory.
Get-ChildItem c:\scripts -file | Group-Object -Property {
switch -Regex ($_.extension) {
"\.(ps(d|m)?1(xml)?)$" {"PowerShell"}
"\.(bat|cmd|sh)$" {"Shell"}
"\.(txt|md)$" {"Text"}
"\.(xml|json|csv|yml)$" {"Data"}
"\.(zip|rar)$" { "Archive"}
Default {"Other"}
}
}
The grouping is using a Switch statement. If you aren't familiar with it, start with reading the about_Switch help topic. I am looking at the extension property and doing a regular expression comparison. Switch statements don't have to be this complex, but this one is. If the file matches a pattern, the output value will be the corresponding string.
As an alternative, you could use an If/ElseIF statement. Remember that in a Switch statement, PowerShell tests for all possible matches. If you don't want that behavior, and there is a chance you might get multiple matches, you can use Break.
"\.(ps(d|m)?1(xml)?)$" {"PowerShell";Break}
In my case, I wrote the regex patterns to avoid duplicate matching. And even though the Default entry isn't required, when grouping like this I would include it. Otherwise, the grouping result name will be blank. And in some cases, like when grouping certain Active Directory objects, will throw an exception.
Here's a final example using Active Directory. I want to organize users by department into larger "buckets".
$Operations = "IT", "Dev", "Accounting", "Executive", "Manufacturing", "Payroll", "Quality Assurance"
$Sales = "Sales", "Marketing"
$Internal = "Physical Plant", "Development"
$Customer = "Customer Service", "Consumer Affairs", "Public Affairs"
Get-ADUser -Filter * -Properties title, department, Displayname |
Group-Object -property {
$vars = Get-Variable -Name "Operations", "Sales", "Internal", "Customer"
if ($_.department) {
foreach ($v in $vars) {
if ($v.value -contains $_.department) {
$class = $v.name
Break
}
} #foreach
}
if (-not $class) {
$class = "Uncategorized"
}
$class
}
I define variables that contain the department names. The grouping uses a custom property that loops through the variables and tests if the department name belongs to the variable. If so, I'm using the variable name as the output. Not that I'm taking into consideration accounts with no department and those that might not be in my list. Without this, I get an "Object reference not to to an instance of an object" error.
Summary
I'm always telling people to focus on "objects in the pipeline" when working with PowerShell. This is a great example. Obviously start with the objects you have, but don't let that stop you from using objects in creative ways. I'm always looking for opportunities to do more and custom grouping properties is a handy tool for your PowerShell toolbox.