I\'m running the Test-AdfsServerHealth
(Ref.)
The problem is, one of the output values (value name Output
) is an array that shows up as
tl;dr:
Test-AdfsServerHealth |
Select-Object Name, Result, Detail, @{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ExportTo-Csv out.csv
The above serializes each .Output
hashtable's entries into single-line string composed of space-separated
pairs (PSv4+ syntax) that should work reasonably well in CSV output.
Since CSV is a text format, PowerShell serializes objects to be exported by calling their .ToString()
method.
Complex objects such as [hashtable]
instances often yield just their full type name (System.Collections.Hashtable
) for .ToString()
, which isn't useful in a CSV.
A simplified example (I'm using ConvertTo-Csv
, but the example applies analogously to Export-Csv
):
# Create a custom object whose .col2 property is a hashtable with 2
# sample entries and convert it to CSV
PS> [pscustomobject] @{ prop1 = 1; Output = @{ name='foo'; ID=666 } } | ConvertTo-Csv
"prop1","Output"
"1","System.Collections.Hashtable"
If all output objects from Test-AdfsServerHealth
had the same hashtable structure in their .Output
property, you could try to flatten the hashtable by making its entries columns in their own right, but it sounds like that is not the case.
You must therefore manually transform the hashtable into a text representation that fits into a single CSV column:
You can do this with Select-Object
and a calculated property that performs the transformation for you, but you need to decide on a text representation that makes sense in the context of a CSV file.
In the following example, a single-line string composed of space-separated
pairs is created (PSv4+ syntax).
[pscustomobject] @{ prop1 = 1; Output = @{ name='foo'; ID=666 } } |
Select-Object prop1, @{
n='Output'
e={ $_.prop2.GetEnumerator().ForEach({ '{0}={1}' -f $_.Key, $_.Value }) -join ' ' }
} | ConvertTo-Csv
For an explanation of the hashtable format that creates the calculated prop2
property, see this answer of mine.
The above yields:
"prop1","prop2"
"1","ID=666 name=foo"
Note, however, that if the values in your hashtables are again complex objects that serialize to their type name only, you'd have to apply the approach recursively.
If the hashtable-valued properties of the objects to export to a CSV file all have the same structure, you can opt to make the hashtable entries each their own output column.
Let's take the following sample input: a collection of 2 custom objects whose .prop2
value is a hashtable with a uniform set of keys (entries):
$coll = [pscustomobject] @{ prop1 = 1; prop2 = @{ name='foo1'; ID=666 } },
[pscustomobject] @{ prop1 = 2; prop2 = @{ name='foo2'; ID=667 } }
If you know the key names (of interest) up front, you can simply use an explicit list of calculated properties to create the individual columns:
$coll | select prop1, @{ n='name'; e={ $_.prop2.name } }, @{ n='ID'; e={ $_.prop2.ID } } |
ConvertTo-Csv
The above yields the following, showing that the hashtable entries became their own columns, name
and ID
:
"prop1","name","ID"
"1","foo1","666"
"2","foo2","667"
More advanced techniques are required if you do not know the key names up front:
# Create the list of calculated properties dynamically, from the 1st input
# object's .prop2 hashtable.
$propList = foreach ($key in $coll[0].prop2.Keys) {
# The script block for the calculated property must be created from a
# *string* in this case, so we can "bake" the key name into it.
@{ n=$key; e=[scriptblock]::Create("`$_.prop2.$key") }
}
$coll | Select-Object (, 'prop1' + $propList) | ConvertTo-Csv
This yields the same output as the previous command with the fixed list of calculated properties.