Before you create the file inventory
You do not need administrator rights to inventory folders your Windows account can already read. Run PowerShell normally unless you deliberately need to inspect a protected location.
Choose two locations:
- Source folder: the folder whose files you want to list.
- Output CSV file: where the inventory will be saved.
Warning: The CSV can expose filenames and full folder paths, which may reveal project names, account names, or other sensitive information. Store and share the CSV accordingly.
Also save the CSV outside the folder you are scanning. If you save it inside the source folder, it can become part of a later inventory and make the result confusing.
Examples:
- Source folder:
C:\Users\Taylor\Documents\Projects - Output CSV:
C:\Users\Taylor\Desktop\Projects-FileList.csv
If either path contains spaces, keep the single quotation marks shown in the commands. PowerShell treats the quoted text as one path.
Create a complete CSV list of files and subfolders
- Open PowerShell.
- On Windows 10, select Start, type
powershell, then open Windows PowerShell. - On Windows 11, select Start, type
powershell, and open Windows PowerShell. Depending on the PC configuration, PowerShell may open in Windows Terminal instead; that is fine.
- On Windows 10, select Start, type
- Copy the following block into PowerShell.
- Change only the two paths at the top:
$SourceFolderand$CsvFile.
Code:$SourceFolder = 'C:\Users\Taylor\Documents\Projects' $CsvFile = 'C:\Users\Taylor\Desktop\Projects-FileList.csv' $CsvFolder = Split-Path -LiteralPath $CsvFile -Parent if (-not (Test-Path -LiteralPath $SourceFolder -PathType Container)) { throw "The source folder does not exist: $SourceFolder" } if (-not (Test-Path -LiteralPath $CsvFolder -PathType Container)) { throw "The folder for the CSV does not exist: $CsvFolder" } if (Test-Path -LiteralPath $CsvFile) { throw "The CSV already exists. Change the file name or move the existing file: $CsvFile" } $Inventory = @( Get-ChildItem -LiteralPath $SourceFolder -File -Recurse -Force ` -ErrorAction SilentlyContinue -ErrorVariable ScanErrors | Select-Object ` Name, Extension, @{Name='SizeBytes'; Expression={$_.Length}}, @{Name='SizeMB'; Expression={[math]::Round($_.Length / 1MB, 2)}}, LastWriteTime, FullName | Sort-Object FullName ) if ($Inventory.Count -eq 0) { throw "No accessible files were found in: $SourceFolder" } $Inventory | Export-Csv -LiteralPath $CsvFile -NoTypeInformation -Encoding UTF8 "Created $($Inventory.Count) file records:" $CsvFile if ($ScanErrors.Count -gt 0) { "" "Some items could not be read. Review the first 10 errors with:" '$ScanErrors | Select-Object -First 10' } - Press Enter after pasting the full block. If PowerShell shows a continuation prompt beginning with
>>, it is still waiting for an unfinished command. Press Ctrl+C, then paste the complete block again. - Wait for PowerShell to display the number of file records created and the CSV path. A large folder tree, network location, or external drive can take more than a few seconds.
The resulting CSV has these columns:
| Column | What it contains |
|---|---|
Name | Filename, including its extension |
Extension | File extension such as .docx, .jpg, or .pdf |
SizeBytes | Exact file size in bytes |
SizeMB | File size in megabytes, rounded to two decimal places |
LastWriteTime | The file’s last-modified date and time |
FullName | Complete folder path and filename |
The -Recurse parameter tells PowerShell to scan the selected folder and all subfolders. The -File parameter excludes folders from the CSV so every row represents a file. The -Force parameter includes hidden files where your account has permission to read them; it does not bypass Windows security permissions.
Verify the inventory before opening it in Excel
First, confirm that PowerShell created a file at the output location:
Test-Path -LiteralPath $CsvFile -PathType Leaf
The expected result is:
True
Next, load the CSV back into PowerShell and count its rows:
$ImportedInventory = Import-Csv -LiteralPath $CsvFile
$ImportedInventory.Count
The returned number should match the file-record count shown when you created the inventory.
To inspect the first five entries directly in PowerShell, run:
$ImportedInventory |
Select-Object -First 5 Name, Extension, SizeMB, LastWriteTime, FullName |
Format-Table -AutoSize
This is only a preview. Do not add Format-Table before Export-Csv in the original inventory command. Formatting cmdlets create display-oriented output rather than the structured file objects that Export-Csv needs.
To open the CSV in its default associated application, such as Excel, run:
Invoke-Item -LiteralPath $CsvFile
You can also open File Explorer, browse to the CSV, and double-click it.
In Excel, select any cell in the list and use Data > Filter to add filter arrows to the header row. You can then sort by SizeMB, filter by an extension such as .pdf, or search the FullName column for a folder or filename fragment.
Create a quick on-screen folder list instead
If you only need to inspect the folder contents and do not need a CSV file, use this shorter command:
Get-ChildItem -LiteralPath 'C:\Users\Taylor\Documents\Projects' -File -Recurse -Force |
Select-Object Name, Extension, Length, LastWriteTime, FullName |
Sort-Object FullName |
Format-Table -AutoSize
Change the path to your own source folder. This displays files in the PowerShell window but does not save anything.
For a top-level listing only, remove -Recurse:
Get-ChildItem -LiteralPath 'C:\Users\Taylor\Documents\Projects' -File -Force |
Select-Object Name, Extension, Length, LastWriteTime, FullName |
Sort-Object Name |
Format-Table -AutoSize
That version lists files directly inside the selected folder but ignores files stored in its subfolders.
Adjust the inventory for a narrower search
For a very large folder tree, it is often better to create a targeted inventory rather than scan everything.
List only PDF files
Replace the Get-ChildItem line in the main script with this version:
Get-ChildItem -LiteralPath $SourceFolder -File -Recurse -Force -Filter '*.pdf' `
-ErrorAction SilentlyContinue -ErrorVariable ScanErrors |
Keep the remaining Select-Object, Sort-Object, and Export-Csv lines unchanged.
List only files modified after a specific date
Add this line immediately after the $CsvFile line:
$ChangedAfter = Get-Date '2026-01-01'
Then add this pipeline stage between Get-ChildItem and Select-Object:
Where-Object { $_.LastWriteTime -gt $ChangedAfter } |
The relevant part of the command becomes:
Get-ChildItem -LiteralPath $SourceFolder -File -Recurse -Force `
-ErrorAction SilentlyContinue -ErrorVariable ScanErrors |
Where-Object { $_.LastWriteTime -gt $ChangedAfter } |
Select-Object `
Use an unambiguous date format such as 2026-01-01, not a regional format such as 1/1/26.
Use your regional CSV separator
Most Windows installations use commas in CSV files, and Excel normally opens those files correctly. If your spreadsheet program places all data into one column because it expects a semicolon or another regional list separator, change the export line to:
$Inventory | Export-Csv -LiteralPath $CsvFile -NoTypeInformation -UseCulture -Encoding UTF8
Use a new output filename or move the existing CSV first, because the script intentionally stops rather than overwriting an existing inventory.
Troubleshoot common inventory problems
PowerShell says the source folder does not exist
Check the value assigned to $SourceFolder:
$SourceFolder
Test-Path -LiteralPath $SourceFolder -PathType Container
The second command must return True. Confirm that the path points to a folder, not a file, and that you retained the enclosing quotation marks. A path with spaces, such as C:\Users\Taylor\OneDrive - Contoso\Documents, must remain quoted.
Use -LiteralPath as shown rather than changing it to -Path. Literal paths prevent PowerShell from treating characters such as [ and ] as wildcard patterns.
The script says the CSV already exists
This is intentional protection against overwriting a previous inventory. Change the output filename in $CsvFile, for example:
$CsvFile = 'C:\Users\Taylor\Desktop\Projects-FileList-October.csv'
Alternatively, move the older CSV to an archive folder before running the script again.
The inventory is missing files
The main script includes hidden files through -Force, but it cannot read locations for which your account lacks permission. It also does not use administrator privileges to override file-system security.
Check whether the scan encountered inaccessible locations:
$ScanErrors | Select-Object -First 10
If the errors identify a folder you are authorized to inspect, run PowerShell using an account that has access to that folder. Do not run PowerShell as administrator solely to inventory ordinary personal folders.
Also note that recursive enumeration does not normally follow directory symbolic links. This helps avoid unexpected loops or scanning the same redirected location repeatedly.
The CSV opens as one long column in Excel
Create a new CSV using the -UseCulture export method shown earlier. That tells PowerShell to use the list separator configured for the current Windows regional settings.
If the file already exists, do not remove it blindly. Rename it, move it, or choose a different output name before rerunning the command.
The CSV is empty or PowerShell reports no accessible files
Confirm that the source folder actually contains files and that you did not accidentally use the output folder as the source. For a quick visible check, run:
Get-ChildItem -LiteralPath $SourceFolder -File -Force
If that command produces no results, the folder contains no files directly inside it. Add -Recurse to check its subfolders:
Get-ChildItem -LiteralPath $SourceFolder -File -Recurse -Force
The inventory takes too long
Scanning a large drive, a disconnected network share, a cloud-synced folder with placeholder files, or a folder containing hundreds of thousands of files can take significant time. Press Ctrl+C to stop the command; no completed CSV is guaranteed when a running export is interrupted.
For a more manageable inventory, scan a narrower source folder, omit -Recurse to exclude subfolders, or use -Filter to inventory one extension at a time.