How to Write a Variable to a CSV File in PowerShell

To write a variable to a CSV file in PowerShell, put the values in a [pscustomobject] and pipe it to Export-Csv. Each property becomes a column:

$row = [pscustomobject]@{ Server = $server; Status = $status }
$row | Export-Csv -Path .\status.csv -NoTypeInformation

Piping a plain string or a hashtable gives you a surprising file instead. Below I show what goes wrong, and how to write strings, arrays, hashtables and loop results the right way. Every example ran in PowerShell 7 and Windows PowerShell 5.1.

Why a string variable writes Length

Export-Csv writes the properties of each object, not its value. A string has only one property, Length, so that’s what you get:

$city = 'Austin'
$city | Export-Csv -Path .\Reports\city.csv -NoTypeInformation
Get-Content -Path .\Reports\city.csv

Output:

"Length"
"6"
PowerShell Export-Csv string variable writes Length
The file holds the length of Austin, not the word (PowerShell 7)

This is the most common Export-Csv question I get. The fix is always to give the value a column name. To see what an object will export, list all its properties.

Write several variables as one row

Build a [pscustomobject] from your variables. The property names become the header, in the order you write them:

$server  = 'NYC-WEB-01'
$status  = 'Running'
$checked = '2026-10-18 09:00'

$row = [pscustomobject]@{ Server = $server; Status = $status; Checked = $checked }
$row | Export-Csv -Path .\Reports\status.csv -NoTypeInformation
Get-Content -Path .\Reports\status.csv

Output:

"Server","Status","Checked"
"NYC-WEB-01","Running","2026-10-18 09:00"

I store the date as text, so it reads the same on every PC. A [datetime] value is written in the computer’s regional format. More object options are in creating objects in PowerShell.

The #TYPE line in Windows PowerShell 5.1

In 5.1, leave out -NoTypeInformation and the first line is a type name. PowerShell 7 dropped it:

$order = [pscustomobject]@{ Order = 'ORD-1001'; City = 'Austin'; Total = 250 }
$order | Export-Csv -Path .\Reports\order.csv
Get-Content -Path .\Reports\order.csv

Output:

"Order","City","Total"
"ORD-1001","Austin","250"

The output above is from PowerShell 7. Windows PowerShell 5.1 writes an extra first line, #TYPE System.Management.Automation.PSCustomObject, which breaks Excel headers. Keep -NoTypeInformation in scripts that may run in 5.1.

Write an array of strings as a column

For a list of values, turn each string into an object with a calculated property:

$cities = 'Austin', 'Denver', 'Seattle'
$cities | Select-Object -Property @{ Name = 'City'; Expression = { $_ } } |
    Export-Csv -Path .\Reports\cities.csv -NoTypeInformation
Get-Content -Path .\Reports\cities.csv

Output:

"City"
"Austin"
"Denver"
"Seattle"

Change ‘City’ to whatever header you need. For a file with a header row and no data yet, see creating CSV files with headers.

Write a hashtable to CSV

PowerShell 7.2 and later export a hashtable’s keys as columns. Windows PowerShell 5.1 exports the hashtable’s own properties instead:

$settings = [ordered]@{ Server = 'NYC-WEB-01'; Port = 443 }
$settings | Export-Csv -Path .\Reports\settings.csv -NoTypeInformation
Get-Content -Path .\Reports\settings.csv

Output:

"Server","Port"
"NYC-WEB-01","443"
PowerShell Export-Csv hashtable in Windows PowerShell 5.1
Keys, Count and SyncRoot instead of the settings (Windows PowerShell 5.1)

The output above is the correct one from PowerShell 7. Cast the hashtable to [pscustomobject] and both versions agree:

$settings = [ordered]@{ Server = 'NYC-WEB-01'; Port = 443 }
[pscustomobject]$settings | Export-Csv -Path .\Reports\settings.csv -NoTypeInformation
Get-Content -Path .\Reports\settings.csv

Output:

"Server","Port"
"NYC-WEB-01","443"

Use [ordered] so the columns keep your order. A plain hashtable can change the order between runs, as shown in dictionaries in PowerShell.

Collect rows in a loop

Return an object from each loop pass, collect them in a variable and export once at the end:

$servers = 'NYC-WEB-01', 'NYC-WEB-02', 'DAL-SQL-01'
$rows = foreach ($name in $servers) {
    [pscustomobject]@{
        Server  = $name
        Site    = $name.Substring(0, 3)
        Checked = '2026-10-18'
    }
}
$rows | Export-Csv -Path .\Reports\servers.csv -NoTypeInformation
Import-Csv -Path .\Reports\servers.csv | Format-Table

Output:

Server     Site Checked
------     ---- -------
NYC-WEB-01 NYC  2026-10-18
NYC-WEB-02 NYC  2026-10-18
DAL-SQL-01 DAL  2026-10-18
PowerShell write variables to CSV in a loop
Three server rows written and read back with Import-Csv (PowerShell 7)

Exporting once is faster than writing inside the loop, because the file is opened only one time. To read the rows back one by one, see looping through CSV rows.

Append a row to an existing CSV

-Append adds rows below the existing ones. The new object must have the same columns, or Export-Csv stops:

[pscustomobject]@{ Order = 'ORD-1001'; Total = 250 } | Export-Csv -Path .\Reports\orders.csv -NoTypeInformation
[pscustomobject]@{ Order = 'ORD-1002'; Total = 75 }  | Export-Csv -Path .\Reports\orders.csv -NoTypeInformation -Append

try {
    [pscustomobject]@{ Order = 'ORD-1003' } | Export-Csv -Path .\Reports\orders.csv -NoTypeInformation -Append -ErrorAction Stop
}
catch { "Failed. $($_.Exception.Message)" }

[pscustomobject]@{ Order = 'ORD-1003'; Note = 'Rush' } | Export-Csv -Path .\Reports\orders.csv -NoTypeInformation -Append -Force
Get-Content -Path .\Reports\orders.csv

Output:

Failed. Cannot append CSV content to the following file: .\Reports\orders.csv. The appended object does not have a property that corresponds to the following column: Total. To continue with mismatched properties, add the -Force parameter, and then retry the command.
"Order","Total"
"ORD-1001","250"
"ORD-1002","75"
"ORD-1003",

With -Force, the last command appended ORD-1003 an