How can I specify the column ordering when exporting to CSV in PowerShell?
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+):
$Record = [ordered] @{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
This guarantees that the [pscustomobject]
instances later created by the New-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 let 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 unpredictable order (the order is an implementation detail and not guaranteed), 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.:
PS> [pscustomobject] @{ a = 1; b = 2; c = 3 } # key order is PRESERVED
a b c
- - -
1 2 3
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:
[pscustomobject] [ordered] @{ a = 1; b = 2; c = 3 } # [ordered] is optional
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:
New-Object PSCustomObject -Property @{ a = 1; b = 2; c = 3 } # !! order NOT preserved
$ht = @{ a = 1; b = 2; c = 3 }; [pscustomobject] $ht # !! order NOT preserved
[pscustomobject] (@{ a = 1; b = 2; c = 3 }) # !! order NOT preserved, due to (...)
Therefore, when not casting a hashtable literal directly to [pscustomobject]
, define it with [ordered]
explicitly.
This should work...
$Table |
Select-Object "Group Name", "Name", "Username" |
Export-Csv "C:\temp\SecurityGroups.csv" -NoTypeInformation