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 with Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP.

By Hazik

Setup

Install Nylas CLI and authenticate. See the PowerShell email guide for full install options.

irm https://cli.nylas.com/install.ps1 | iex
nylas auth config  # paste your API key

Filter attachments by file type

Don't download everything. Filter to specific file types using Where-Object on the filename extension.

# 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

Gmail allows attachments up to 25 MB per message. Outlook supports up to 150 MB. Check size before downloading to avoid filling your disk.

# 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

According to Verizon's 2024 DBIR, 43% of malware is delivered via email attachments. Scan every download before processing.

# 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

Download all attachments of a specific type from recent emails. Organize into date-based folders with duplicate handling.

# 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

The PnP.PowerShell module uploads files to SharePoint document libraries. Combine with Nylas CLI to auto-save invoice PDFs.

# 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

If OneDrive is syncing, save files to the sync folder. They upload automatically.

# 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

Before downloading anything, audit what's in your inbox.

# 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"

Frequently asked questions

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