Add PowerShell version: sf-org-lic.ps1
- Complete PowerShell implementation of the license reporting tool - Full PowerShell parameter handling with Get-Help integration - Error handling consistent with PowerShell patterns - Color-coded output using PowerShell Write-Host - Updated README.md to document both Unix and PowerShell versions - Added PowerShell examples and installation instructions - Maintains feature parity with Bash version Cross-platform support: - sf-org-lic (Bash/Unix) - sf-org-lic.ps1 (PowerShell/Windows)
This commit is contained in:
@@ -20,7 +20,7 @@ Authentication:
|
||||
|
||||
Org and metadata:
|
||||
- **[`sf-org-create` / `sf-org-create.ps1`](#sf-org-create)** - Smart scratch org creation
|
||||
- **[`sf-org-lic`](#sf-org-lic)** - Salesforce org license utilization report
|
||||
- **[`sf-org-lic` / `sf-org-lic.ps1`](#sf-org-lic--sf-org-licps1)** - Salesforce org license utilization report
|
||||
- **[`sf-org-info` / `sf-org-info.ps1`](#sf-org-info--sf-org-infops1)** - Quick org info, limits, and context
|
||||
- **[`sf-retrieve` / `sf-retrieve.ps1`](#sf-retrieve--sf-retrieveps1)** - Streamlined metadata retrieval (types, manifest, packages)
|
||||
|
||||
@@ -176,7 +176,7 @@ sf-org-create -al TestOrg -dd 5 -nn
|
||||
|
||||
---
|
||||
|
||||
### <a id="sf-org-lic"></a>[🏠](#salesforce-cli-wrapper-scripts) sf-org-lic
|
||||
### <a id="sf-org-lic--sf-org-licps1"></a>[🏠](#salesforce-cli-wrapper-scripts) sf-org-lic / sf-org-lic.ps1
|
||||
|
||||
Generate comprehensive Salesforce license utilization reports.
|
||||
|
||||
@@ -184,6 +184,9 @@ Generate comprehensive Salesforce license utilization reports.
|
||||
```bash
|
||||
sf-org-lic -to ORG [-hp]
|
||||
```
|
||||
```powershell
|
||||
sf-org-lic.ps1 -to "PROD-ORG"
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `-to` - Target org alias or username (required)
|
||||
|
||||
249
sf-org-lic.ps1
Normal file
249
sf-org-lic.ps1
Normal file
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Salesforce org license utilization reporting tool
|
||||
|
||||
.DESCRIPTION
|
||||
Generate comprehensive Salesforce license utilization reports for an org.
|
||||
Reports on User Licenses and Permission Set Licenses with detailed totals
|
||||
and professional formatting suitable for license audits.
|
||||
|
||||
.PARAMETER to
|
||||
Target org alias or username (required)
|
||||
|
||||
.PARAMETER hp
|
||||
Show this help message
|
||||
|
||||
.EXAMPLE
|
||||
.\sf-org-lic.ps1 -to "PROD-ORG"
|
||||
Generate license report for production org
|
||||
|
||||
.EXAMPLE
|
||||
.\sf-org-lic.ps1 -to "admin@company.com"
|
||||
Generate report for specific user
|
||||
|
||||
.EXAMPLE
|
||||
.\sf-org-lic.ps1 -hp
|
||||
Show help message
|
||||
|
||||
.NOTES
|
||||
This script requires:
|
||||
- Salesforce CLI (sf)
|
||||
- PowerShell 5.1+ or PowerShell Core
|
||||
- Valid authentication to the target org
|
||||
|
||||
Features:
|
||||
- User Licenses - Core Salesforce user license utilization
|
||||
- Permission Set Licenses - Add-on feature license usage
|
||||
- Comprehensive totals - Overall usage summary with remaining capacity
|
||||
- Clean formatting - Professional tabular output
|
||||
- Error handling - Clear messages for invalid orgs with suggestions
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$to = "",
|
||||
[switch]$hp
|
||||
)
|
||||
|
||||
function Show-Help {
|
||||
Get-Help $MyInvocation.MyCommand.Path -Detailed
|
||||
}
|
||||
|
||||
function Write-Error-And-Exit {
|
||||
param([string]$Message, [int]$ExitCode = 1)
|
||||
Write-Host "Error: $Message" -ForegroundColor Red
|
||||
exit $ExitCode
|
||||
}
|
||||
|
||||
# Show help if requested or if no org specified
|
||||
if ($hp) {
|
||||
Show-Help
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($to -eq "") {
|
||||
Write-Host "Usage: .\sf-org-lic.ps1 -to <ORG_ALIAS>" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "Generate Salesforce license utilization report for an org"
|
||||
Write-Host ""
|
||||
Write-Host "Examples:"
|
||||
Write-Host " .\sf-org-lic.ps1 -to PROD-ORG"
|
||||
Write-Host " .\sf-org-lic.ps1 -to dev@company.com"
|
||||
Write-Host " .\sf-org-lic.ps1 -hp"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for required dependencies
|
||||
try {
|
||||
Get-Command sf -ErrorAction Stop | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-Error-And-Exit "'sf' CLI is required but not found. Please install Salesforce CLI." 1
|
||||
}
|
||||
|
||||
# Validate org exists and is authorized
|
||||
Write-Host "Validating org: $to..." -ForegroundColor Yellow
|
||||
try {
|
||||
$orgCheckResult = & sf org display --target-org $to --json 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
# Try to get available orgs for suggestions
|
||||
try {
|
||||
$orgListResult = & sf org list --json 2>$null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
$orgList = $orgListResult | ConvertFrom-Json
|
||||
$availableOrgs = $orgList.result.other | ForEach-Object { $_.alias } | Where-Object { $_ -ne $null } | Sort-Object
|
||||
$orgSuggestions = $availableOrgs -join ", "
|
||||
if ($orgSuggestions -eq "") { $orgSuggestions = "none" }
|
||||
}
|
||||
else {
|
||||
$orgSuggestions = "none"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$orgSuggestions = "none"
|
||||
}
|
||||
Write-Error-And-Exit "org alias '$to' not found or not authorized. Available orgs: $orgSuggestions" 2
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error-And-Exit "Failed to validate org: $_" 2
|
||||
}
|
||||
|
||||
# Helper function to run SOQL queries with error handling
|
||||
function Invoke-SafeSOQLQuery {
|
||||
param([string]$Query)
|
||||
|
||||
try {
|
||||
$result = & sf data query --target-org $to --json --query $Query 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$jsonResult = $result | ConvertFrom-Json
|
||||
|
||||
# Check if result contains error information
|
||||
if ($jsonResult.name -or $jsonResult.error -or ($jsonResult.message -and ($jsonResult.commandName -or $jsonResult.status))) {
|
||||
$errorMsg = $jsonResult.message -or $jsonResult.error -or "Unknown error"
|
||||
|
||||
# Check if it's a "not supported" error (non-fatal)
|
||||
if ($errorMsg -like "*not supported*" -or $errorMsg -like "*does not exist*" -or $errorMsg -like "*INVALID_TYPE*") {
|
||||
return "NOT_AVAILABLE"
|
||||
}
|
||||
|
||||
Write-Error-And-Exit "SOQL query failed: $errorMsg" 4
|
||||
}
|
||||
|
||||
return $jsonResult
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
# Helper function to format table output
|
||||
function Format-LicenseTable {
|
||||
param(
|
||||
[object]$JsonData,
|
||||
[string[]]$Headers,
|
||||
[string]$LicenseType
|
||||
)
|
||||
|
||||
$records = $JsonData.result.records
|
||||
if (-not $records) { $records = $JsonData.records }
|
||||
if (-not $records) { $records = @() }
|
||||
|
||||
# Print headers
|
||||
Write-Host ($Headers -join "`t") -ForegroundColor Cyan
|
||||
Write-Host ("-" * ($Headers -join "`t").Length) -ForegroundColor Cyan
|
||||
|
||||
if ($records.Count -eq 0) {
|
||||
Write-Host "(no rows)"
|
||||
Write-Host ""
|
||||
return @{ Total = 0; Used = 0 }
|
||||
}
|
||||
|
||||
$totalLicenses = 0
|
||||
$totalUsed = 0
|
||||
|
||||
foreach ($record in $records) {
|
||||
if ($LicenseType -eq "User") {
|
||||
$total = [int]($record.TotalLicenses -or 0)
|
||||
$used = [int]($record.UsedLicenses -or 0)
|
||||
$remaining = $total - $used
|
||||
Write-Host "$($record.Name)`t$total`t$used`t$remaining"
|
||||
}
|
||||
elseif ($LicenseType -eq "PSL") {
|
||||
$total = [int]($record.TotalLicenses -or 0)
|
||||
$used = [int]($record.UsedLicenses -or 0)
|
||||
$remaining = $total - $used
|
||||
Write-Host "$($record.MasterLabel)`t$($record.DeveloperName)`t$total`t$used`t$remaining"
|
||||
}
|
||||
|
||||
$totalLicenses += $total
|
||||
$totalUsed += $used
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
$remaining = $totalLicenses - $totalUsed
|
||||
Write-Host "Totals: Total=$totalLicenses Used=$totalUsed Remaining=$remaining" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
return @{ Total = $totalLicenses; Used = $totalUsed }
|
||||
}
|
||||
|
||||
# Main execution
|
||||
Write-Host ""
|
||||
Write-Host "Salesforce License Utilization — $to" -ForegroundColor White -BackgroundColor Blue
|
||||
Write-Host "Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# SOQL queries for the two main license types
|
||||
$userLicenseQuery = "SELECT Id, Name, TotalLicenses, UsedLicenses FROM UserLicense ORDER BY Name LIMIT 200"
|
||||
$pslQuery = "SELECT Id, MasterLabel, DeveloperName, TotalLicenses, UsedLicenses FROM PermissionSetLicense ORDER BY MasterLabel LIMIT 200"
|
||||
|
||||
$grandTotalLicenses = 0
|
||||
$grandTotalUsed = 0
|
||||
|
||||
# 1) User Licenses
|
||||
Write-Host "User Licenses" -ForegroundColor White -BackgroundColor DarkBlue
|
||||
Write-Host "-------------" -ForegroundColor White -BackgroundColor DarkBlue
|
||||
|
||||
$userLicenseResult = Invoke-SafeSOQLQuery -Query $userLicenseQuery
|
||||
if ($userLicenseResult -eq "NOT_AVAILABLE") {
|
||||
Write-Host "UserLicense object not available in this org."
|
||||
Write-Host ""
|
||||
}
|
||||
elseif ($userLicenseResult -ne $null) {
|
||||
$userTotals = Format-LicenseTable -JsonData $userLicenseResult -Headers @("Name", "Total", "Used", "Remaining") -LicenseType "User"
|
||||
$grandTotalLicenses += $userTotals.Total
|
||||
$grandTotalUsed += $userTotals.Used
|
||||
}
|
||||
else {
|
||||
Write-Host "Failed to query UserLicense."
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# 2) Permission Set Licenses
|
||||
Write-Host "Permission Set Licenses" -ForegroundColor White -BackgroundColor DarkBlue
|
||||
Write-Host "-----------------------" -ForegroundColor White -BackgroundColor DarkBlue
|
||||
|
||||
$pslResult = Invoke-SafeSOQLQuery -Query $pslQuery
|
||||
if ($pslResult -eq "NOT_AVAILABLE") {
|
||||
Write-Host "PermissionSetLicense object not available in this org."
|
||||
Write-Host ""
|
||||
}
|
||||
elseif ($pslResult -ne $null) {
|
||||
$pslTotals = Format-LicenseTable -JsonData $pslResult -Headers @("MasterLabel", "DeveloperName", "Total", "Used", "Remaining") -LicenseType "PSL"
|
||||
$grandTotalLicenses += $pslTotals.Total
|
||||
$grandTotalUsed += $pslTotals.Used
|
||||
}
|
||||
else {
|
||||
Write-Host "Failed to query PermissionSetLicense."
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host "Note: This report covers the main Salesforce license types available in most orgs." -ForegroundColor Gray
|
||||
Write-Host "FeatureLicense objects are not commonly available and have been excluded." -ForegroundColor Gray
|
||||
Reference in New Issue
Block a user