I'm writing a script in Powershell
that exports all securitygroups
and their members
from Active Directory
. Now I want to format the output of the .csv
.
The Code:
$Groups = Get-ADGroup -Properties * -Filter * -SearchBase "OU=SERVICES,DC=XXXXXX,DC=XXXXX"
$Table = @()
$Record = @{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
Foreach($G In $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $G -recursive | select name,samaccountname
Foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $G.Name
$Record."Name" = $Member.name
$Record."UserName" = $Member.samaccountname
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
}
$Table | export-csv "C:\temp\SecurityGroups.csv" -NoTypeInformation
The Result:
"Username","Name","Group Name"
"aman","Ani Manoukian","Commercial"
"adan","Aurelia Danneels","Commercial"
"kdeb","Kathleen De Backer","Commercial"
"TVGR","Thijs Van Grimbergen","Commercial"
"SVDE","Sofie Van den Eynde","Commercial"
Now I want the output formatted as:
"Group Name","Name","Username"
instead of "Username","Name","Group Name"
Can anyone help me please?
Thanks in advance.
gvee's helpful answer is a pragmatic solution that ensures that the columns appear in the desired order, because the order in which you pass property names to
Select-Object
is the order in which the properties are added to the resulting[pscustomobject]
instances.It is, however, inefficient, because the desired column order can be ensured at the time
$Record
is defined, without needing an additional pipeline stage that effectively duplicates the result objects:Define
$Record
as an ordered hashtable as follows (requires PSv3+):This guarantees that the
[pscustomobject]
instances later created by theNew-Object PSObject -property $Record
calls contain properties in the same order as the keys were defined in$Record
.Two asides:
*
New-Object PSObject -property $Record
could be simplified to[pscustomobject] $Record
* Building up a large array incrementally is more efficiently handled with a
[System.Collections.ArrayList]
instance to which you add elements with.Add()
rather than using PowerShell's built-in arrays with+=
, which creates a copy of the array every time. Even better is to et PowerShell create the array for you, simply by capturing the output from yourforeach
loop in a variable ($Table = foreach ...
- see this answer)Supplemental information:
The source of the problem is that regular hashtables (
[hashtable]
instances) enumerate their keys in an effectively random order (the order is an implementation detail), and when you create a[pscustomobject]
from a hashtable, that unpredictable key ordering is reflected in the ordering of the resulting object's properties.By contrast, in PSv3+ you can create an ordered hashtable by placing the
[ordered]
keyword before a hashtable literal, which results in a[System.Collections.Specialized.OrderedDictionary]
instance whose keys are ordered based on the order in which they were added.Creating a
[pscustomobject]
instance from an ordered hashtable then preserves that key ordering in the resulting object's properties.Note that PowerShell v3+ offers a convenient shortcut for creating a
[pscustomobject]
instance from a hashtable using a cast; e.g.:Note how the key-definition order was preserved, even though
[ordered]
was not specified.In other words: When you cast a hashtable literal directly to
[pscustomobject]
,[ordered]
is implied, so the above is equivalent to:Caveat: This implicit ordering only applies when a hashtable literal is directly cast to
[pscustomboject]
, so the key/property order is not preserved in the following variations:Therefore, when not casting a hashtable literal directly to
[pscustomobject]
, define it with[ordered]
explicitly.This should work...