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 keyFilter 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 -ForceAuto-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
- Parse and extract data from emails -- extract orders, tickets, structured data
- Email and calendar automation -- combine email and calendar workflows
- Send email from PowerShell -- compose and send with attachments
- Full command reference -- every flag and subcommand