AI & Agents

How to Extract File Metadata with PowerShell on Windows

PowerShell ships with every Windows 10 and 11 machine, making it the fastest way to read file metadata without installing anything. This guide covers four approaches, from Get-ItemProperty for basic timestamps to Shell.Application COM objects that expose over 300 extended properties, System.Drawing for image EXIF data, and recursive scripts that process entire folder trees into CSV reports.

Fast.io Editorial Team 10 min read
Terminal window showing PowerShell metadata extraction commands and output

What PowerShell Metadata Extraction Gives You

Every file on a Windows system carries properties beyond its visible content. A JPEG holds GPS coordinates and camera settings. A Word document records its author, revision count, and total editing time. An MP4 stores codec details, frame rate, and audio channels. Windows indexes all of this through the Windows Property System, and PowerShell gives you programmatic access to it.

PowerShell metadata extraction uses native Windows cmdlets and .NET classes to read file properties like author, dates, dimensions, and EXIF data without requiring third-party software installation. Because PowerShell is pre-installed on all Windows 10 and 11 systems, you can start extracting metadata on any modern Windows machine without downloading a single tool.

There are four main approaches, each suited to different depth levels:

  • Get-Item and Get-ChildItem return basic filesystem properties like size, creation date, last modified, and extension
  • Get-ItemProperty targets specific named properties, including registry values
  • Shell.Application COM object exposes the full Windows Property System, the same extended details you see in Explorer's Details tab
  • System.Drawing .NET classes decode image-specific EXIF data like camera model, focal length, and GPS coordinates

The right choice depends on what you need. For a quick timestamp check, Get-Item is enough. For a full metadata audit across hundreds of files, you want the Shell.Application approach combined with a recursive script.

Basic File Properties with Get-Item and Get-ChildItem

The simplest way to read file metadata in PowerShell is the Get-Item cmdlet. It returns a FileInfo object with standard filesystem properties.

$file = Get-Item "C:\Photos\vacation.jpg"
$file | Select-Object Name, Length, CreationTime, LastWriteTime, LastAccessTime, Extension

This gives you the file name, size in bytes, three timestamps, and the extension. For a directory listing with metadata, Get-ChildItem does the same thing across multiple files:

Get-ChildItem "C:\Photos" -File | Select-Object Name, Length, LastWriteTime | Sort-Object LastWriteTime -Descending

You can filter by extension, size, or date range directly in the pipeline:

Get-ChildItem "C:\Documents" -File -Recurse |
    Where-Object { $_.Extension -eq ".pdf" -and $_.Length -gt 1MB } |
    Select-Object FullName, @{Name="SizeMB"; Expression={[math]::Round($_.Length / 1MB, 2)}}, LastWriteTime

These cmdlets return standard .NET FileInfo properties: Name, FullName, Directory, Extension, Length, CreationTime, CreationTimeUtc, LastAccessTime, LastWriteTime, Attributes, IsReadOnly, and more. You can see the full list by piping a file to Get-Member:

Get-Item "C:\Photos\vacation.jpg" | Get-Member -MemberType Property

The limitation is obvious: Get-Item and Get-ChildItem only return filesystem-level metadata. They cannot read EXIF tags, document authors, video codecs, or any of the extended properties that Windows Explorer shows in its Details tab. For those, you need the Shell.Application COM object.

Extended Properties with Shell.Application COM Object

Windows Shell properties expose over 300 metadata fields per file through COM objects. This is the same data you see when you right-click a file in Explorer and open the Details tab. PowerShell can access all of it through the Shell.Application COM object and its GetDetailsOf method.

Here is the core pattern:

$shell = New-Object -ComObject Shell.Application
$folder = $shell.Namespace("C:\Photos")
$file = $folder.ParseName("vacation.jpg")

# Get a specific property by index
$dateTaken = $folder.GetDetailsOf($file, 12)
$cameraModel = $folder.GetDetailsOf($file, 30)
$dimensions = $folder.GetDetailsOf($file, 31)

The tricky part is knowing which index maps to which property. Property indices are not documented in a single official list, and they can vary slightly between Windows versions. To discover available properties on your system, enumerate all indices and print the ones that have names:

$shell = New-Object -ComObject Shell.Application
$folder = $shell.Namespace("C:\Photos")

0..350 | ForEach-Object {
    $name = $folder.GetDetailsOf($null, $_)
    if ($name) {
        [PSCustomObject]@{
            Index = $_
            Name  = $name
        }
    }
} | Format-Table -AutoSize

Running this on a typical Windows 11 machine returns property names like Name (0), Size (1), Item type (2), Date modified (3), Date created (4), Authors (20), Title (21), Camera model (30), Dimensions (31), Date taken (12), and dozens more. The exact count depends on your Windows version and installed codecs.

To extract all non-empty properties for a single file:

$shell = New-Object -ComObject Shell.Application
$folder = $shell.Namespace("C:\Photos")
$file = $folder.ParseName("vacation.jpg")

$metadata = @{}
0..350 | ForEach-Object {
    $name = $folder.GetDetailsOf($null, $_)
    $value = $folder.GetDetailsOf($file, $_)
    if ($name -and $value) {
        $metadata[$name] = $value
    }
}

$metadata | Format-Table -AutoSize

This approach works for any file type Windows recognizes. The Shell.Application COM object returns different property sets depending on the file format. An MP3 file exposes artist, album, genre, and bitrate. A Word document shows author, page count, word count, and template. A video file returns frame rate, resolution, and codec.

One caveat: GetDetailsOf returns strings, not typed values. Dates come back as formatted strings, file sizes include units like "2.4 MB", and durations use "HH:MM:SS" format. If you need to sort or compare these values programmatically, you will need to parse them.

Detailed property view showing extended file metadata fields
Fastio features

Extract and query file metadata without writing parsers

Fast.io Metadata Views turn documents into a searchable, filterable database. Describe the fields you need in plain English, and structured data appears automatically. 50 GB free, no credit card required.

Image EXIF Data with System.Drawing

The Shell.Application approach works for EXIF data, but it returns human-readable strings. If you need raw numeric values from image files, like exact GPS coordinates, shutter speed fractions, or ISO values, the System.Drawing .NET namespace gives you direct access to EXIF property items.

In Windows PowerShell 5.1 (the version pre-installed on Windows 10 and 11), System.Drawing is available without any additional installation:

Add-Type -AssemblyName System.Drawing
$image = [System.Drawing.Image]::FromFile("C:\Photos\vacation.jpg")

# List all EXIF property IDs
$image.PropertyItems | ForEach-Object {
    [PSCustomObject]@{
        Id    = "0x{0:X4}" -f $_.Id
        Type  = $_.Type
        Len   = $_.Len
        Value = if ($_.Type -eq 2) { [System.Text.Encoding]::ASCII.GetString($_.Value).Trim("`0") } else { $_.Value }
    }
}

$image.Dispose()

Each PropertyItem has an Id (matching EXIF tag numbers), a Type (indicating the data format), a Length, and a raw byte array Value. Common EXIF tag IDs include:

  • 0x010F: Camera manufacturer
  • 0x0110: Camera model
  • 0x0132: Date/time original
  • 0x829A: Exposure time
  • 0x829D: F-number
  • 0x8827: ISO speed
  • 0x920A: Focal length
  • 0x0002/0x0004: GPS latitude/longitude

Here is a more targeted function that extracts the most commonly needed EXIF fields:

function Get-ImageExif {
    param([string]$Path)

Add-Type -AssemblyName System.Drawing
    $image = [System.Drawing.Image]::FromFile((Resolve-Path $Path))
    $result = [ordered]@{ File = (Split-Path $Path -Leaf) }

$tagMap = @{
        0x010F = "Make"
        0x0110 = "Model"
        0x0132 = "DateTime"
        0x829A = "ExposureTime"
        0x829D = "FNumber"
        0x8827 = "ISO"
        0x920A = "FocalLength"
        0x0100 = "ImageWidth"
        0x0101 = "ImageHeight"
    }

foreach ($tag in $tagMap.GetEnumerator()) {
        try {
            $prop = $image.GetPropertyItem($tag.Key)
            if ($prop.Type -eq 2) {
                $result[$tag.Value] = [System.Text.Encoding]::ASCII.GetString($prop.Value).Trim("`0")
            } elseif ($prop.Type -eq 5 -and $prop.Len -eq 8) {
                $num = [BitConverter]::ToUInt32($prop.Value, 0)
                $den = [BitConverter]::ToUInt32($prop.Value, 4)
                $result[$tag.Value] = "$num/$den"
            } elseif ($prop.Type -eq 3) {
                $result[$tag.Value] = [BitConverter]::ToUInt16($prop.Value, 0)
            } else {
                $result[$tag.Value] = $prop.Value
            }
        } catch {
            $result[$tag.Value] = $null
        }
    }

$image.Dispose()
    [PSCustomObject]$result
}

Get-ImageExif "C:\Photos\vacation.jpg"

Note that PowerShell 7+ on .NET Core does not include System.Drawing by default. If you are using PowerShell 7, you need the System.Drawing.Common NuGet package, or you can use the Shell.Application COM approach instead, which works in both PowerShell 5.1 and 7.

For dedicated PowerShell modules, PSReadExif reads EXIF data using native GDI+ classes and is available from the PowerShell Gallery via Install-Module PSReadExif.

Batch Extraction Across Entire Folders

Single-file extraction is useful for spot checks, but real work usually involves processing hundreds or thousands of files. Combining Get-ChildItem with the Shell.Application COM object gives you a recursive batch extraction script that outputs to CSV.

function Export-FolderMetadata {
    param(
        [string]$FolderPath,
        [string]$OutputCsv = "metadata-export.csv",
        [string[]]$Extensions = @("*")
    )

$shell = New-Object -ComObject Shell.Application
    $results = @()

# Get property names from the first folder
    $sampleFolder = $shell.Namespace($FolderPath)
    $propertyNames = @{}
    0..50 | ForEach-Object {
        $name = $sampleFolder.GetDetailsOf($null, $_)
        if ($name) { $propertyNames[$_] = $name }
    }

$files = Get-ChildItem -Path $FolderPath -File -Recurse
    if ($Extensions -ne "*") {
        $files = $files | Where-Object { $Extensions -contains $_.Extension.ToLower() }
    }

foreach ($file in $files) {
        $folder = $shell.Namespace($file.DirectoryName)
        $shellFile = $folder.ParseName($file.Name)
        $row = [ordered]@{ FullPath = $file.FullName }

foreach ($prop in $propertyNames.GetEnumerator()) {
            $value = $folder.GetDetailsOf($shellFile, $prop.Key)
            $row[$prop.Value] = $value
        }

$results += [PSCustomObject]$row
        Write-Progress -Activity "Reading metadata" -Status $file.Name -PercentComplete (($results.Count / $files.Count) * 100)
    }

$results | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
    Write-Host "Exported $($results.Count) files to $OutputCsv"
}

# Extract metadata from all JPEGs and PNGs in a folder tree
Export-FolderMetadata -FolderPath "C:\Photos" -OutputCsv "photo-metadata.csv" -Extensions @(".jpg", ".jpeg", ".png")

A few notes on this script. The property index range (0..50) covers the most common fields while keeping execution fast. Expand to 0..350 if you need every possible property, but expect slower performance on large directories. The Write-Progress call shows a progress bar in the terminal, which helps when processing thousands of files.

For very large directories, the array-append pattern ($results +=) becomes slow because PowerShell copies the entire array on each addition. A more performant approach uses an ArrayList or outputs objects directly to the pipeline:

$files | ForEach-Object {
    $folder = $shell.Namespace($_.DirectoryName)
    $shellFile = $folder.ParseName($_.Name)
    $row = [ordered]@{ FullPath = $_.FullName }
    foreach ($prop in $propertyNames.GetEnumerator()) {
        $row[$prop.Value] = $folder.GetDetailsOf($shellFile, $prop.Key)
    }
    [PSCustomObject]$row
} | Export-Csv -Path "metadata-export.csv" -NoTypeInformation -Encoding UTF8

This streams objects through the pipeline instead of accumulating them in memory, which handles folders with tens of thousands of files without running out of RAM.

Dashboard view showing batch-processed file metadata in a structured format

When PowerShell Scripts Hit Their Limits

PowerShell metadata extraction is powerful for local, ad hoc work. You can read file properties, build CSV reports, and automate folder audits without installing anything. But it has real limitations that surface as your needs grow.

First, PowerShell runs on the machine where the files live. If your team stores files across multiple machines, cloud drives, or shared network volumes, each person runs their own script against their own copy. There is no shared, queryable view of extracted metadata.

Second, extending what you extract means editing code. When someone asks "can we also pull the contract expiration date from these PDFs?" you are writing a new parser or shelling out to a tool like ExifTool. The Shell.Application COM object reads what Windows indexes, but it cannot interpret document content.

Third, results live in flat CSV files. Sorting and filtering requires reopening the CSV in Excel or writing more PowerShell to parse it. There is no persistent, team-accessible database of extracted metadata.

For local automation, external tools can extend PowerShell's reach. ExifTool handles over 400 file formats and can be called from PowerShell with & exiftool -json "C:\Photos\vacation.jpg" | ConvertFrom-Json. TagLib# (taglib-sharp) is a .NET library for audio metadata that you can load directly into PowerShell sessions.

For team-scale metadata extraction, cloud workspace tools remove the scripting overhead entirely. Fast.io's Metadata Views let you describe the fields you want in plain English, like "extract the author, creation date, and page count from every PDF in this workspace." The system designs a typed schema with field types like Text, Integer, Date, and Boolean, matches files automatically, and populates a sortable, filterable spreadsheet. You don't write a parser. Adding a new column does not require reprocessing existing files.

This is a different approach than PowerShell scripting. Instead of writing code to read specific properties from specific file formats, you tell the system what information you need and it handles the extraction. It works across PDFs, images, Word documents, spreadsheets, presentations, and scanned pages, including handwritten notes.

Other cloud-based alternatives exist. AWS Textract focuses on document text and table extraction. Google Document AI handles forms and invoices. Azure AI Document Intelligence covers similar ground. Fast.io combines structured extraction with workspace features like file versioning, granular permissions, and Intelligence Mode for semantic search, all on a free plan with 50 GB of storage and 5,000 monthly credits.

For agent-driven workflows, Metadata Views are accessible through the Fast.io MCP server, so an AI agent can create extraction schemas, trigger processing, and query results programmatically. The combination of PowerShell for local file audits and a workspace tool for team-wide metadata management covers both the scripting and collaboration sides of the problem.

Frequently Asked Questions

How do I get file metadata in PowerShell?

Use Get-Item for basic properties like size, dates, and extension. For extended metadata like author, camera model, and dimensions, create a Shell.Application COM object and call GetDetailsOf on the file. The COM approach exposes over 300 properties per file, the same data visible in Windows Explorer's Details tab.

Can PowerShell read EXIF data from photos?

Yes. Two approaches work. The Shell.Application COM object returns EXIF fields as readable strings through GetDetailsOf with property indices like 12 (date taken), 30 (camera model), and 31 (dimensions). For raw numeric EXIF values, use System.Drawing in Windows PowerShell 5.1 to access PropertyItems directly, which gives you typed data like GPS coordinates, shutter speed fractions, and ISO numbers.

How to extract metadata from all files in a folder with PowerShell?

Combine Get-ChildItem with the -Recurse flag and the Shell.Application COM object. Pipe Get-ChildItem output through a ForEach-Object block that calls GetDetailsOf for each file, then export the results with Export-Csv. For large directories, stream objects through the pipeline instead of collecting them in an array to avoid memory issues.

What PowerShell command shows file properties?

Get-Item returns a FileInfo object with standard properties. Pipe it to Format-List or Select-Object to see specific fields. Get-ItemProperty retrieves named properties and is commonly used for registry values. For extended file properties beyond the filesystem level, you need the Shell.Application COM object, which is not a single cmdlet but a three-line pattern using New-Object, Namespace, and GetDetailsOf.

Does PowerShell 7 support System.Drawing for EXIF extraction?

Not by default. PowerShell 7 runs on .NET Core, which does not include System.Drawing. You can install the System.Drawing.Common NuGet package to restore this functionality, or use the Shell.Application COM object approach instead, which works in both PowerShell 5.1 and 7 on Windows. The PSReadExif module from the PowerShell Gallery is another option that handles EXIF extraction without System.Drawing.

What is the difference between Get-ItemProperty and Shell.Application for metadata?

Get-ItemProperty reads named properties from PowerShell providers, primarily filesystem attributes and registry values. It returns standard properties like LastWriteTime, CreationTime, and Length. Shell.Application is a COM object that accesses the Windows Property System, exposing over 300 extended properties including author, camera model, dimensions, bitrate, and document statistics. Use Get-ItemProperty for quick filesystem checks and Shell.Application for rich metadata extraction.

Related Resources

Fastio features

Extract and query file metadata without writing parsers

Fast.io Metadata Views turn documents into a searchable, filterable database. Describe the fields you need in plain English, and structured data appears automatically. 50 GB free, no credit card required.