#!/usr/bin/env pwsh <# .SYNOPSIS Data export wrapper for Salesforce CLI with SOQL query support .DESCRIPTION A user-friendly wrapper around 'sf data export' that simplifies data export from Salesforce orgs with SOQL query support, multiple formats, and intelligent defaults. .PARAMETER Query SOQL query to export data (alias: -qy) .PARAMETER File File containing SOQL query (alias: -fl) .PARAMETER SObject Standard object query (exports common fields) (alias: -so) .PARAMETER Output Output file path (default: export.csv) (alias: -ot) .PARAMETER TargetOrg Target org username or alias (alias: -to) .PARAMETER Format Output format: csv, json (default: csv) (alias: -fm) .PARAMETER Bulk Use bulk API for large datasets (alias: -bk) .PARAMETER Wait Wait time in minutes (default: 10) (alias: -wt) .PARAMETER Verbose Enable verbose output (alias: -ve) .PARAMETER Help Show this help message (alias: -hp) .EXAMPLE .\sf-data-export.ps1 -qy "SELECT Id, Name FROM Account LIMIT 100" .\sf-data-export.ps1 -so Account -fm json -ot accounts.json .\sf-data-export.ps1 -fl queries/contacts.soql -bk -wt 15 .\sf-data-export.ps1 -qy "SELECT Id FROM User" -to production .NOTES This script automatically checks for Salesforce CLI installation and runs diagnostics if the CLI is not found. #> param( [Parameter(ParameterSetName="Query")] [Alias("qy")] [string]$Query, [Parameter(ParameterSetName="File")] [Alias("fl")] [string]$File, [Parameter(ParameterSetName="SObject")] [Alias("so")] [string]$SObject, [Alias("ot")] [string]$Output = "export.csv", [Alias("to")] [string]$TargetOrg, [ValidateSet("csv", "json")] [Alias("fm")] [string]$Format = "csv", [Alias("bk")] [switch]$Bulk, [Alias("wt")] [int]$Wait = 10, [Alias("vb")] [switch]$Verbose, [Alias("hp")] [switch]$Help ) # Show help if requested if ($Help) { Get-Help $MyInvocation.MyCommand.Path -Detailed exit 0 } # Function to check if Salesforce CLI is installed function Test-SalesforceCLI { try { $null = Get-Command sf -ErrorAction Stop return $true } catch { return $false } } # Function to run sf-check diagnostics function Invoke-SalesforceCheck { $checkScript = if (Test-Path "sf-check.ps1") { ".\sf-check.ps1" } elseif (Test-Path "sf-check.sh") { "bash sf-check.sh" } else { $null } if ($checkScript) { Write-Host "Running Salesforce CLI diagnostics..." -ForegroundColor Yellow Invoke-Expression $checkScript } else { Write-Host "Salesforce CLI not found and no diagnostic script available." -ForegroundColor Red Write-Host "Please install the Salesforce CLI: https://developer.salesforce.com/tools/salesforcecli" -ForegroundColor Red } } # Function to build standard object query function New-SObjectQuery { param([string]$SObjectType) switch ($SObjectType) { "Account" { return "SELECT Id, Name, Type, Industry, Phone, Website, BillingCity, BillingState, BillingCountry FROM Account" } "Contact" { return "SELECT Id, FirstName, LastName, Email, Phone, AccountId, Account.Name FROM Contact" } "Lead" { return "SELECT Id, FirstName, LastName, Email, Phone, Company, Status, Source FROM Lead" } "Opportunity" { return "SELECT Id, Name, AccountId, Account.Name, Amount, CloseDate, StageName, Probability FROM Opportunity" } "Case" { return "SELECT Id, CaseNumber, Subject, Status, Priority, Origin, AccountId, Account.Name, ContactId, Contact.Name FROM Case" } "User" { return "SELECT Id, Name, Email, Username, Profile.Name, IsActive, LastLoginDate FROM User" } default { return "SELECT Id, Name FROM $SObjectType" } } } # Function to validate SOQL query function Test-SOQLQuery { param([string]$QueryText) if ($QueryText -notmatch '^\s*SELECT\s+') { Write-Host "Error: Query must start with SELECT" -ForegroundColor Red return $false } return $true } # Silently check for Salesforce CLI if (-not (Test-SalesforceCLI)) { Invoke-SalesforceCheck exit 1 } # Validate that exactly one query method is specified $queryMethods = @($Query, $File, $SObject | Where-Object { $_ }).Count if ($queryMethods -eq 0) { Write-Host "Error: Must specify one of: -Query, -File, or -SObject" -ForegroundColor Red Write-Host "" Write-Host "Usage examples:" -ForegroundColor Yellow Write-Host " .\sf-data-export.ps1 -Query `"SELECT Id, Name FROM Account`"" -ForegroundColor Gray Write-Host " .\sf-data-export.ps1 -File queries/accounts.soql" -ForegroundColor Gray Write-Host " .\sf-data-export.ps1 -SObject Account" -ForegroundColor Gray Write-Host "" Write-Host "Use -Help for detailed usage information." -ForegroundColor Yellow exit 1 } if ($queryMethods -gt 1) { Write-Host "Error: Can only specify one of: -Query, -File, or -SObject" -ForegroundColor Red exit 1 } # Determine the final query $finalQuery = "" if ($Query) { $finalQuery = $Query Write-Host "Using inline query" -ForegroundColor Green } elseif ($File) { if (-not (Test-Path $File)) { Write-Host "Error: Query file not found: $File" -ForegroundColor Red exit 1 } $finalQuery = Get-Content $File -Raw Write-Host "Using query from file: $File" -ForegroundColor Green } elseif ($SObject) { $finalQuery = New-SObjectQuery $SObject Write-Host "Using standard query for $SObject" -ForegroundColor Green } # Validate the query if (-not (Test-SOQLQuery $finalQuery)) { exit 1 } # Build the sf command based on bulk vs regular query if ($Bulk) { # Use Bulk API 2.0 for large datasets $sfArgs = @("data", "export", "bulk", "--query", $finalQuery, "--output-file", $Output, "--result-format", $Format) if ($Wait -ne 10) { $sfArgs += "--wait" $sfArgs += $Wait.ToString() } Write-Host "Using Bulk API 2.0" -ForegroundColor Yellow } else { # Use regular data query for smaller datasets $sfArgs = @("data", "query", "--query", $finalQuery, "--output-file", $Output, "--result-format", $Format) } # Add optional parameters if ($TargetOrg) { $sfArgs += "--target-org" $sfArgs += $TargetOrg Write-Host "Target org: $TargetOrg" -ForegroundColor Cyan } Write-Host "Output format: $Format" -ForegroundColor Cyan Write-Host "Output file: $Output" -ForegroundColor Cyan # Add verbose flag if requested if ($Verbose) { $sfArgs += "--verbose" } # Display export information Write-Host "" Write-Host "📊 Starting Data Export" -ForegroundColor Blue Write-Host "=======================" -ForegroundColor Blue # Show query preview if verbose if ($Verbose) { Write-Host "" Write-Host "📝 SOQL Query:" -ForegroundColor Yellow Write-Host "----------------------------------------" -ForegroundColor Gray Write-Host $finalQuery -ForegroundColor Gray Write-Host "----------------------------------------" -ForegroundColor Gray } # Display the command being run Write-Host "" Write-Host "Executing: sf $($sfArgs -join ' ')" -ForegroundColor Gray Write-Host "" # Execute the command try { & sf @sfArgs $exitCode = $LASTEXITCODE Write-Host "" if ($exitCode -eq 0) { Write-Host "✅ Data export completed successfully!" -ForegroundColor Green # Show file information if it exists if (Test-Path $Output) { $fileInfo = Get-Item $Output $fileSize = if ($fileInfo.Length -gt 1MB) { "{0:N1} MB" -f ($fileInfo.Length / 1MB) } elseif ($fileInfo.Length -gt 1KB) { "{0:N1} KB" -f ($fileInfo.Length / 1KB) } else { "$($fileInfo.Length) bytes" } if ($Format -eq "csv") { # Count records (excluding header) $recordCount = (Get-Content $Output).Count - 1 Write-Host "📁 Exported $recordCount records to $Output ($fileSize)" -ForegroundColor Cyan } else { Write-Host "📁 Data exported to $Output ($fileSize)" -ForegroundColor Cyan } } if ($Verbose) { Write-Host "💡 Use a spreadsheet application or text editor to view the exported data" -ForegroundColor Yellow } } else { Write-Host "❌ Data export failed with exit code: $exitCode" -ForegroundColor Red Write-Host "💡 Check query syntax and permissions" -ForegroundColor Yellow exit $exitCode } } catch { Write-Host "Error executing sf command: $($_.Exception.Message)" -ForegroundColor Red exit 1 }