#!/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 .PARAMETER File File containing SOQL query .PARAMETER SObject Standard object query (exports common fields) .PARAMETER Output Output file path (default: export.csv) .PARAMETER TargetOrg Target org username or alias .PARAMETER Format Output format: csv, json (default: csv) .PARAMETER Bulk Use bulk API for large datasets .PARAMETER Wait Wait time in minutes (default: 10) .PARAMETER Verbose Enable verbose output .PARAMETER Help Show this help message .EXAMPLE .\sf-data-export.ps1 -Query "SELECT Id, Name FROM Account LIMIT 100" .\sf-data-export.ps1 -SObject Account -Format json -Output accounts.json .\sf-data-export.ps1 -File queries/contacts.soql -Bulk -Wait 15 .\sf-data-export.ps1 -Query "SELECT Id FROM User" -TargetOrg production .NOTES This script automatically checks for Salesforce CLI installation and runs diagnostics if the CLI is not found. #> param( [Parameter(ParameterSetName="Query")] [string]$Query, [Parameter(ParameterSetName="File")] [string]$File, [Parameter(ParameterSetName="SObject")] [string]$SObject, [string]$Output = "export.csv", [string]$TargetOrg, [ValidateSet("csv", "json")] [string]$Format = "csv", [switch]$Bulk, [int]$Wait = 10, [switch]$Verbose, [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 $sfArgs = @("data", "export", "--query", $finalQuery) # Add optional parameters if ($TargetOrg) { $sfArgs += "--target-org" $sfArgs += $TargetOrg Write-Host "Target org: $TargetOrg" -ForegroundColor Cyan } if ($Bulk) { $sfArgs += "--bulk" Write-Host "Using Bulk API" -ForegroundColor Yellow } if ($Wait -ne 10) { $sfArgs += "--wait" $sfArgs += $Wait.ToString() } # Set output file and format $sfArgs += "--output-file" $sfArgs += $Output if ($Format -eq "json") { $sfArgs += "--json" } 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 }