Guide

Download Email Attachments in PowerShell

Email attachments account for 43% of all malware delivery, according to Verizon's 2024 Data Breach Investigations Report. This guide shows how to safely download, filter, scan, and process email attachments from PowerShell using Nylas CLI. Filter by file type, enforce size limits, scan with Windows Defender, batch download from multiple messages, and auto-save to SharePoint or OneDrive. Works across all major email providers.

Written by Hazik Director of Product Management

Reviewed by Hazik

VerifiedCLI 3.1.1 · Gmail, Outlook · last tested April 11, 2026

Attachment download methods compared

PowerShell supports three methods for downloading email attachments: Exchange Web Services (EWS), Microsoft Graph API, and Nylas CLI. EWS reaches end-of-life in October 2026, Graph requires Azure AD app registration with Mail.Read scope, and the CLI authenticates with a single command. Each method differs in provider support, auth complexity, and maximum file size handling.

MethodAuth complexityProvidersMax file size
EWS (deprecated Oct 2026)NTLM or OAuth + certificateExchange only10 MB default, 150 MB max
Microsoft Graph SDKAzure AD app + Mail.Read scopeMicrosoft 365 only150 MB (stream for >3 MB)
Nylas CLInylas auth configGmail, Outlook, Exchange, Yahoo, iCloud, IMAPProvider limit (25-150 MB)

With Microsoft Graph, downloading attachments over 3 MB requires the large attachment streaming endpoint — a separate API call with content range headers. The CLI handles this automatically.

Prerequisites

Downloading email attachments from PowerShell requires the Nylas CLI installed and authenticated against your email provider. The PowerShell install script takes under 30 seconds: run irm https://cli.nylas.com/install.ps1 | iex, then authenticate with nylas auth config using your API key. The getting started guide covers all 4 install methods including Homebrew and Go.

Filter attachments by file type

Filtering email attachments by file type in PowerShell prevents downloading unwanted or dangerous files. The Nylas CLI returns attachment metadata including filename, size, and content type in JSON format. PowerShell's Where-Object cmdlet filters on the filename extension before any download begins, so blocked file types never touch the disk.

The script below reads a message with nylas email read, pipes the attachments array through Where-Object, and downloads only matching files. According to CISA's advisory on file-based threats, 6 of the top 10 malware file types are Windows-executable extensions (.exe, .bat, .cmd, .scr, .vbs, .js). The blocked-extensions list covers all 6.

# Download only PDFs from a specific message
$msg = nylas email read <message-id> --json | ConvertFrom-Json

$msg.attachments |
    Where-Object { $_.filename -like "*.pdf" } |
    ForEach-Object {
        Write-Host "Downloading PDF: $($_.filename) ($([math]::Round($_.size/1024))KB)"
        nylas email attachments download $_.id $msg.id
    }

# Download images only
$msg.attachments |
    Where-Object { $_.filename -match "\.(jpg|jpeg|png|gif|webp)$" } |
    ForEach-Object { nylas email attachments download $_.id $msg.id }

# Download spreadsheets and documents
$msg.attachments |
    Where-Object { $_.filename -match "\.(xlsx|xls|csv|docx|doc)$" } |
    ForEach-Object { nylas email attachments download $_.id $msg.id }

# Block dangerous file types
$blocked = @('.exe', '.bat', '.cmd', '.ps1', '.vbs', '.js', '.msi', '.scr')
$msg.attachments |
    Where-Object {
        $ext = [IO.Path]::GetExtension($_.filename).ToLower()
        $ext -notin $blocked
    } |
    ForEach-Object { nylas email attachments download $_.id $msg.id }

Enforce size limits

Enforcing attachment size limits in PowerShell prevents large files from consuming disk space or slowing down processing pipelines. Gmail caps attachments at 25 MB per message, while Outlook allows up to 150 MB. The Nylas CLI exposes the size field in bytes on each attachment object, so PowerShell can compare against a configurable threshold before downloading.

The following script accepts a $MaxSizeMB parameter (defaulting to 10 MB), lists recent messages with attachments, and skips any file exceeding the limit. Skipped files print a yellow warning with the actual size and the configured maximum.

# Download attachments under a size limit
param([int]$MaxSizeMB = 10)

$maxBytes = $MaxSizeMB * 1024 * 1024
$emails = nylas email list --has-attachment --json --limit 20 | ConvertFrom-Json

foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json
    foreach ($att in $detail.attachments) {
        if ($att.size -gt $maxBytes) {
            Write-Host "SKIP: $($att.filename) ($([math]::Round($att.size/1MB, 1))MB > $($MaxSizeMB)MB)" `
                -ForegroundColor Yellow
            continue
        }
        Write-Host "Downloading: $($att.filename) ($([math]::Round($att.size/1KB))KB)"
        nylas email attachments download $att.id $detail.id
    }
}

Scan downloads with Windows Defender

Scanning email attachments with Windows Defender before processing catches malware at the point of entry. According to Verizon's 2024 Data Breach Investigations Report, 43% of malware is delivered via email attachments. PowerShell's Start-MpScan cmdlet triggers a custom scan on a specific file path, and Get-MpThreatDetection returns any threats detected during that scan.

The script below downloads each attachment to a staging directory, scans it with Windows Defender, and moves any flagged file to a quarantine folder. It also blocks 6 high-risk extensions (.exe, .bat, .cmd, .scr, .vbs, .js) before the download starts. This two-layer approach -- extension blocking plus antivirus scanning -- stops both known and zero-day threats.

# safe-download.ps1 -- Download and scan attachments
param(
    [string]$OutputDir = "C:\Downloads\email-attachments",
    [string]$QuarantineDir = "C:\Downloads\quarantine"
)

foreach ($dir in @($OutputDir, $QuarantineDir)) {
    if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
}

$emails = nylas email list --has-attachment --json --limit 10 | ConvertFrom-Json

foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json

    foreach ($att in $detail.attachments) {
        # Skip blocked extensions
        $ext = [IO.Path]::GetExtension($att.filename).ToLower()
        if ($ext -in @('.exe', '.bat', '.cmd', '.scr', '.vbs', '.js')) {
            Write-Host "BLOCKED: $($att.filename) (dangerous extension)" -ForegroundColor Red
            continue
        }

        # Download to output directory
        Push-Location $OutputDir
        nylas email attachments download $att.id $detail.id
        Pop-Location

        $filePath = Join-Path $OutputDir $att.filename
        if (-not (Test-Path $filePath)) {
            Write-Host "Download failed: $($att.filename)" -ForegroundColor Red
            continue
        }

        # Scan with Windows Defender
        Write-Host "Scanning: $($att.filename)..." -NoNewline
        Start-MpScan -ScanPath $filePath -ScanType CustomScan 2>&1 | Out-Null

        $threats = Get-MpThreatDetection -ErrorAction SilentlyContinue |
            Where-Object { $_.Resources -contains $filePath }

        if ($threats) {
            Write-Host " THREAT DETECTED" -ForegroundColor Red
            Move-Item $filePath $QuarantineDir -Force
        } else {
            Write-Host " CLEAN" -ForegroundColor Green
        }
    }
}

Batch download into date-based folders

Batch downloading email attachments into date-based folders keeps large collections organized and searchable. The Nylas CLI's nylas email search command accepts a query string and returns matching messages, which PowerShell can iterate to download only attachments matching a file pattern. A typical accounts-payable inbox receives 200-500 invoice PDFs per month, so date-based subfolders and duplicate filename handling prevent collisions.

The script below searches for emails matching a keyword (defaulting to "invoice"), filters attachments by a glob pattern (defaulting to *.pdf), and saves each file into a YYYY-MM-DD subfolder. When a filename already exists, it appends a counter -- invoice (1).pdf, invoice (2).pdf -- to avoid overwrites.

# batch-download.ps1 -- Batch download with organization
param(
    [string]$SearchQuery = "invoice",
    [string]$FilePattern = "*.pdf",
    [int]$Limit = 50,
    [string]$BaseDir = "C:\Downloads\invoices"
)

$emails = nylas email search $SearchQuery --json --limit $Limit | ConvertFrom-Json
$totalFiles = 0

foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json
    $matching = $detail.attachments | Where-Object { $_.filename -like $FilePattern }
    if ($matching.Count -eq 0) { continue }

    # Create date-based subfolder
    $dateStr = ($email.date -split 'T')[0]
    $dateDir = Join-Path $BaseDir $dateStr
    if (-not (Test-Path $dateDir)) { New-Item -ItemType Directory -Path $dateDir | Out-Null }

    Push-Location $dateDir
    foreach ($att in $matching) {
        # Handle duplicate filenames
        $targetName = $att.filename
        $counter = 1
        while (Test-Path (Join-Path $dateDir $targetName)) {
            $base = [IO.Path]::GetFileNameWithoutExtension($att.filename)
            $ext = [IO.Path]::GetExtension($att.filename)
            $targetName = "$base ($counter)$ext"
            $counter++
        }
        nylas email attachments download $att.id $detail.id
        $totalFiles++
    }
    Pop-Location
}

Write-Host "Downloaded $totalFiles files to $BaseDir"

Auto-save to SharePoint

Auto-saving email attachments to SharePoint eliminates the manual download-then-upload cycle that costs finance teams 5-10 minutes per invoice. The PnP.PowerShell module provides the Add-PnPFile cmdlet to upload files directly to SharePoint document libraries. Combining PnP.PowerShell with Nylas CLI creates a pipeline: search for invoices, download PDFs to a temp directory, upload to SharePoint, then clean up.

The script below connects to a SharePoint site with interactive auth, searches for emails containing "invoice", downloads PDF attachments to a staging folder, and uploads each file into a month-based subfolder (e.g., Invoices/2026-05). PnP.PowerShell requires one-time install via Install-Module and Azure AD admin consent for the PnP Management Shell app.

# Install PnP.PowerShell (one time)
Install-Module PnP.PowerShell -Scope CurrentUser -Force

# sharepoint-save.ps1 -- Download invoices, upload to SharePoint
param(
    [string]$SiteUrl = "https://company.sharepoint.com/sites/Finance",
    [string]$LibraryName = "Invoices"
)

Connect-PnPOnline -Url $SiteUrl -Interactive

$emails = nylas email search "invoice" --has-attachment --json --limit 20 |
    ConvertFrom-Json

$tempDir = "$env:TEMP\invoice-staging"
if (-not (Test-Path $tempDir)) { New-Item -ItemType Directory -Path $tempDir | Out-Null }

foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json
    $pdfs = $detail.attachments | Where-Object { $_.filename -like "*.pdf" }

    foreach ($pdf in $pdfs) {
        Push-Location $tempDir
        nylas email attachments download $pdf.id $detail.id
        Pop-Location

        $localPath = Join-Path $tempDir $pdf.filename
        if (Test-Path $localPath) {
            $dateFolder = (Get-Date $email.date).ToString('yyyy-MM')
            Add-PnPFile -Path $localPath -Folder "$LibraryName/$dateFolder"
            Write-Host "Uploaded: $($pdf.filename) -> $LibraryName/$dateFolder" -ForegroundColor Green
            Remove-Item $localPath
        }
    }
}

Remove-Item $tempDir -Recurse -Force

Auto-save to OneDrive sync folder

Saving email attachments to a OneDrive sync folder is the simplest cloud-upload approach -- no API calls, no authentication beyond the existing OneDrive desktop client. OneDrive for Business syncs files within 2 minutes of write according to Microsoft's sync documentation, so attachments appear in the cloud almost immediately. The sync folder path follows the pattern $env:USERPROFILE\OneDrive - Company\ on Windows machines joined to an organization.

The script below lists the 10 most recent emails with attachments, downloads each attachment directly into a OneDrive subfolder, and lets the desktop client handle the upload. No PnP.PowerShell module or SharePoint connection is required.

# onedrive-save.ps1 -- Save attachments to OneDrive sync folder
$onedrivePath = "$env:USERPROFILE\OneDrive - Company\Email Attachments"
if (-not (Test-Path $onedrivePath)) {
    New-Item -ItemType Directory -Path $onedrivePath | Out-Null
}

$emails = nylas email list --has-attachment --json --limit 10 | ConvertFrom-Json

foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json
    Push-Location $onedrivePath
    foreach ($att in $detail.attachments) {
        nylas email attachments download $att.id $detail.id
        Write-Host "Saved to OneDrive: $($att.filename)"
    }
    Pop-Location
}

Write-Host "Files will sync to OneDrive automatically"

Audit attachment types and sizes

Auditing attachment types and sizes before downloading gives a clear picture of what's in your inbox and how much disk space the download will consume. A typical business mailbox accumulates 50-100 attachments per week across PDFs, spreadsheets, images, and documents. Running an audit first helps identify unexpected file types (potentially malicious .exe or .js files) and estimate total download size.

The script below reads the last 100 emails with attachments, collects filename extension and size for each file, and prints a grouped summary showing count and total megabytes per file type. The Group-Object cmdlet handles the aggregation, and Measure-Object sums the sizes within each group.

# attachment-audit.ps1 -- Summarize attachments by type and size
$emails = nylas email list --has-attachment --json --limit 100 | ConvertFrom-Json

$all = foreach ($email in $emails) {
    $detail = nylas email read $email.id --json | ConvertFrom-Json
    foreach ($att in $detail.attachments) {
        [PSCustomObject]@{
            Extension = [IO.Path]::GetExtension($att.filename).ToLower()
            SizeKB = [math]::Round($att.size / 1024, 1)
            Filename = $att.filename
            From = $email.from
        }
    }
}

Write-Host "File Type Summary:" -ForegroundColor Cyan
$all | Group-Object Extension | Sort-Object Count -Descending |
    ForEach-Object {
        $mb = [math]::Round(($_.Group | Measure-Object -Property SizeKB -Sum).Sum / 1024, 1)
        Write-Host "  $($_.Name.PadRight(8)) $($_.Count) files ($mb MB)"
    }

$totalMB = [math]::Round(($all | Measure-Object -Property SizeKB -Sum).Sum / 1024, 1)
Write-Host "Total: $($all.Count) attachments, $totalMB MB"

Troubleshooting

The three most common issues when downloading email attachments in PowerShell are stale attachment IDs producing 0-byte files, Windows Defender access-denied errors when scanning without elevation, and Azure AD consent failures with PnP.PowerShell. Each has a specific fix that resolves the problem in under 2 minutes.

Download produces a 0-byte file

The attachment ID or message ID is stale. Re-fetch the message with nylas email read <id> --json to get current attachment IDs. Attachment IDs change if the message is moved between folders.

Start-MpScan : Access denied

Windows Defender scanning requires administrator privileges. Run PowerShell as Administrator, or use & "C:\Program Files\Windows Defender\MpCmdRun.exe" -Scan -ScanType 3 -File $filePath which works without elevation.

PnP.PowerShell fails with Connect-PnPOnline : AADSTS50001

The PnP Management Shell app needs admin consent in your tenant. Ask your Azure AD admin to grant consent, or register a custom app with Sites.ReadWrite.All and use Connect-PnPOnline -ClientId <id> -Interactive.

Frequently asked questions

These 4 questions cover the most common scenarios for downloading email attachments in PowerShell: filtering by file type, scanning for viruses, auto-saving to SharePoint, and understanding provider-specific size limits. Each answer includes the exact PowerShell command or cmdlet needed.

How do I filter email attachments by file type in PowerShell?

Read the message with nylas email read <id> --json | ConvertFrom-Json, then filter: $msg.attachments | Where-Object { $_.filename -like "*.pdf" }. Use -match with regex for multiple types: $_.filename -match "\.(pdf|docx|xlsx)$".

Can I scan email attachments for viruses before saving?

Yes. After downloading, scan with Windows Defender: Start-MpScan -ScanPath $filePath -ScanType CustomScan. Check for detections with Get-MpThreatDetection and quarantine if needed. On Linux, use ClamAV.

How do I auto-save attachments to SharePoint?

Download with Nylas CLI, then upload with PnP.PowerShell: Add-PnPFile -Path $localPath -Folder "Library/Folder". For OneDrive, save to the local sync folder and it uploads automatically.

Is there a size limit for downloading attachments?

Nylas CLI downloads any attachment the provider accepts. Gmail allows 25 MB per message, Outlook 150 MB. Check $att.size before downloading to skip files above your threshold.


Next steps