Skip to content

Instantly share code, notes, and snippets.

@nctiggy
Created April 24, 2026 18:50
Show Gist options
  • Select an option

  • Save nctiggy/d05248be9f46878d347be39375eacea4 to your computer and use it in GitHub Desktop.

Select an option

Save nctiggy/d05248be9f46878d347be39375eacea4 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Automated RVTools export for VMware environment assessment.
.DESCRIPTION
Two-step workflow:
1. Run against each vCenter/ESXi host to collect exports (run as many times as needed)
2. Run with -Zip to package everything up for emailing
Downloads and installs RVTools automatically if not already installed.
.PARAMETER Server
vCenter or ESXi hostname/IP to export from. Omit when using -Zip.
.PARAMETER User
vSphere username (e.g. administrator@vsphere.local).
If omitted, uses Windows pass-through authentication.
.PARAMETER Password
vSphere password. If -User is specified without -Password, you will be prompted.
.PARAMETER OutputDir
Directory for exports. Defaults to Desktop\RVTools-Export.
All runs accumulate here until you zip.
.PARAMETER Format
Export format: "xlsx" (default) or "csv".
.PARAMETER Zip
Package all collected exports into a zip file for emailing. No server connection needed.
.EXAMPLE
.\Export-RVTools.ps1 -Server vcenter01.company.com
# Uses Windows pass-through auth (no prompt) and exports a single vCenter.
.EXAMPLE
.\Export-RVTools.ps1 -Server vcenter.company.com -User admin@vsphere.local
# Prompts for password via secure input, then exports.
.EXAMPLE
.\Export-RVTools.ps1 -Server esxi-standalone.company.com -User root
# Targets a standalone ESXi host.
.EXAMPLE
.\Export-RVTools.ps1 -Zip
# After running one or more exports, bundle everything into a zip for email.
.EXAMPLE
.\Export-RVTools.ps1 -Zip -OutputDir C:\Exports
# Zip exports from a custom directory.
#>
param(
[Parameter(Mandatory=$false, HelpMessage="vCenter or ESXi hostname")]
[string]$Server,
[Parameter(Mandatory=$false)]
[string]$User,
[Parameter(Mandatory=$false)]
[string]$Password,
[Parameter(Mandatory=$false)]
[string]$OutputDir = (Join-Path ([Environment]::GetFolderPath('Desktop')) "RVTools-Export"),
[Parameter(Mandatory=$false)]
[ValidateSet("xlsx","csv")]
[string]$Format = "xlsx",
[Parameter(Mandatory=$false)]
[switch]$Zip
)
$ErrorActionPreference = "Stop"
# --- Configuration ---
$RVToolsInstallerUrl = "https://downloads.dell.com/rvtools/rvtools4.7.1.msi"
$RVToolsPaths = @(
"${env:ProgramFiles(x86)}\Dell\RVTools\RVTools.exe",
"$env:ProgramFiles\Dell\RVTools\RVTools.exe",
"${env:ProgramFiles(x86)}\Robware\RVTools\RVTools.exe",
"$env:ProgramFiles\Robware\RVTools\RVTools.exe"
)
$TempInstaller = Join-Path $env:TEMP "RVTools-installer.msi"
# --- Functions ---
function Find-RVTools {
foreach ($path in $RVToolsPaths) {
if (Test-Path $path) { return $path }
}
$inPath = Get-Command RVTools.exe -ErrorAction SilentlyContinue
if ($inPath) { return $inPath.Source }
return $null
}
function Get-RVToolsEncryptedPassword {
param(
[Parameter(Mandatory=$true)][string]$PlainPassword
)
# RVTools uses Windows DPAPI (ConvertFrom-SecureString) with a "_RVToolsV3PWD" prefix.
# The ciphertext is bound to this Windows user + machine, which is fine because
# RVTools.exe runs in the same session immediately after.
# See: C:\Program Files (x86)\Dell\RVTools\RVToolsPasswordEncryption.ps1
$secure = ConvertTo-SecureString -String $PlainPassword -AsPlainText -Force
$encrypted = $secure | ConvertFrom-SecureString
return "_RVToolsV3PWD" + $encrypted
}
function Install-RVTools {
Write-Host "`n[*] RVTools not found. Downloading installer..." -ForegroundColor Yellow
# Check .NET Framework 4.6.2+
$ndpKey = "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"
if (Test-Path $ndpKey) {
$release = (Get-ItemProperty $ndpKey).Release
if ($release -lt 394802) {
Write-Host "[!] .NET Framework 4.6.2+ required. Current release: $release" -ForegroundColor Red
Write-Host " Download from: https://dotnet.microsoft.com/download/dotnet-framework" -ForegroundColor Red
exit 1
}
} else {
Write-Host "[!] .NET Framework 4.x not detected. RVTools requires .NET 4.6.2+" -ForegroundColor Red
exit 1
}
# Download
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($RVToolsInstallerUrl, $TempInstaller)
Write-Host "[+] Downloaded to $TempInstaller" -ForegroundColor Green
} catch {
Write-Host "[!] Download failed: $_" -ForegroundColor Red
Write-Host " Download manually from https://www.robware.net/rvtools/" -ForegroundColor Yellow
exit 1
}
# Install silently (MSI)
Write-Host "[*] Installing RVTools (silent)..." -ForegroundColor Yellow
$msiArgs = @("/i", "`"$TempInstaller`"", "/qn", "REBOOT=ReallySuppress", "/norestart")
$proc = Start-Process -FilePath "msiexec.exe" -ArgumentList $msiArgs -Wait -PassThru
if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) {
Write-Host "[!] Install failed (exit code $($proc.ExitCode)). Try running installer manually." -ForegroundColor Red
exit 1
}
Remove-Item $TempInstaller -Force -ErrorAction SilentlyContinue
$exe = Find-RVTools
if (-not $exe) {
Write-Host "[!] Install completed but RVTools.exe not found at expected paths." -ForegroundColor Red
Write-Host " Check: $($RVToolsPaths -join ', ')" -ForegroundColor Yellow
exit 1
}
Write-Host "[+] RVTools installed: $exe" -ForegroundColor Green
return $exe
}
# --- Zip Mode ---
if ($Zip) {
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " RVTools Export - Package for Delivery" -ForegroundColor Cyan
Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray
Write-Host "============================================" -ForegroundColor Cyan
if (-not (Test-Path $OutputDir)) {
Write-Host "`n[!] No export directory found at: $OutputDir" -ForegroundColor Red
Write-Host " Run exports first before zipping." -ForegroundColor Yellow
exit 1
}
$exports = Get-ChildItem -Path $OutputDir -File
if ($exports.Count -eq 0) {
Write-Host "`n[!] No export files found in: $OutputDir" -ForegroundColor Red
Write-Host " Run exports first before zipping." -ForegroundColor Yellow
exit 1
}
# Show what we're packaging
Write-Host "`n[*] Files to package:" -ForegroundColor Yellow
$totalSize = 0
foreach ($f in $exports) {
$sizeKB = [math]::Round($f.Length / 1KB, 1)
$totalSize += $f.Length
Write-Host " $($f.Name) ($sizeKB KB)" -ForegroundColor DarkGray
}
# Create zip
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$zipName = "RVTools-Export_${timestamp}.zip"
$zipPath = Join-Path (Split-Path $OutputDir) $zipName
Compress-Archive -Path "$OutputDir\*" -DestinationPath $zipPath -Force
$zipSizeMB = [math]::Round((Get-Item $zipPath).Length / 1MB, 2)
Write-Host "`n============================================" -ForegroundColor Green
Write-Host " Package Ready" -ForegroundColor Green
Write-Host "============================================" -ForegroundColor Green
Write-Host " Files: $($exports.Count) exports packaged" -ForegroundColor White
Write-Host " Zip: $zipPath" -ForegroundColor White
Write-Host " Size: $zipSizeMB MB" -ForegroundColor White
Write-Host "`n Please email the zip file to:" -ForegroundColor Yellow
Write-Host " craig.smith@spectrocloud.com" -ForegroundColor Cyan
Write-Host ""
exit 0
}
# --- Export Mode ---
if (-not $Server) {
$desktop = [Environment]::GetFolderPath('Desktop')
Write-Host "Usage:" -ForegroundColor Yellow
Write-Host " Export: .\Export-RVTools.ps1 -Server <vcenter-or-esxi>" -ForegroundColor White
Write-Host " .\Export-RVTools.ps1 -Server <host> -User <user> # prompts for password" -ForegroundColor White
Write-Host " Zip: .\Export-RVTools.ps1 -Zip" -ForegroundColor White
Write-Host ""
Write-Host "Workflow:" -ForegroundColor Yellow
Write-Host " 1. Run export against each vCenter/ESXi host" -ForegroundColor DarkGray
Write-Host " (Windows pass-through auth is used if you omit -User)" -ForegroundColor DarkGray
Write-Host " 2. Run with -Zip to package everything for email" -ForegroundColor DarkGray
Write-Host ""
Write-Host "Output: $desktop\RVTools-Export\ (zip is written next to that folder)" -ForegroundColor DarkGray
Write-Host "Note: RVTools is downloaded and installed automatically if not present." -ForegroundColor DarkGray
Write-Host " Never pass -Password on the command line; let the script prompt instead." -ForegroundColor DarkGray
Write-Host ""
Get-Help $MyInvocation.MyCommand.Path -Examples
exit 0
}
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " RVTools Export - $Server" -ForegroundColor Cyan
Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray
Write-Host "============================================" -ForegroundColor Cyan
# Find or install RVTools
$rvtools = Find-RVTools
if ($rvtools) {
$version = (Get-Item $rvtools).VersionInfo.FileVersion
Write-Host "[+] RVTools found: $rvtools (v$version)" -ForegroundColor Green
} else {
$rvtools = Install-RVTools
}
# Prompt for password if user specified without password
if ($User -and -not $Password) {
$secPwd = Read-Host -Prompt "Password for $User" -AsSecureString
$Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPwd)
)
}
# Create output directory
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}
# Test connectivity
Write-Host "[*] Testing connectivity to $Server on port 443..." -ForegroundColor Yellow
$test = Test-NetConnection -ComputerName $Server -Port 443 -WarningAction SilentlyContinue
if (-not $test.TcpTestSucceeded) {
Write-Host "[!] Cannot reach $Server on port 443. Check network/firewall." -ForegroundColor Red
exit 1
}
Write-Host "[+] $Server reachable" -ForegroundColor Green
# Build export arguments
$sanitized = $Server -replace "[:/\\]", "_"
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$filename = "${sanitized}_${timestamp}"
$exportCmd = if ($Format -eq "xlsx") { "ExportAll2xlsx" } else { "ExportAll2csv" }
$rvArgs = @(
"-s", $Server,
"-c", $exportCmd,
"-d", $OutputDir,
"-f", "$filename.$Format"
)
if ($User) {
Write-Host "[*] Encrypting password for RVTools..." -ForegroundColor Yellow
$encryptedPassword = Get-RVToolsEncryptedPassword -PlainPassword $Password
$rvArgs += "-u", $User, "-p", $encryptedPassword
} else {
$rvArgs += "-passthroughAuth"
}
# Mask password in display
$displayArgs = $rvArgs -join ' '
if ($Password) { $displayArgs = $displayArgs -replace [regex]::Escape($Password), '****' }
if ($encryptedPassword) { $displayArgs = $displayArgs -replace [regex]::Escape($encryptedPassword), '****' }
Write-Host "[*] Exporting from $Server..." -ForegroundColor Cyan
Write-Host " RVTools.exe $displayArgs" -ForegroundColor DarkGray
# Run export
$proc = Start-Process -FilePath $rvtools -ArgumentList $rvArgs -Wait -PassThru -NoNewWindow
if ($proc.ExitCode -ne 0) {
Write-Host "[!] Export failed for $Server (exit code $($proc.ExitCode))" -ForegroundColor Red
exit 1
}
# Verify output
$exported = Get-ChildItem -Path $OutputDir -Filter "${filename}*" | Sort-Object LastWriteTime -Descending
if ($exported.Count -eq 0) {
Write-Host "[!] No export files found for $Server" -ForegroundColor Red
exit 1
}
# Show results
Write-Host "`n[+] Export complete for $Server" -ForegroundColor Green
foreach ($f in $exported) {
$sizeKB = [math]::Round($f.Length / 1KB, 1)
Write-Host " $($f.Name) ($sizeKB KB)" -ForegroundColor DarkGray
}
# Show accumulated exports
$allExports = Get-ChildItem -Path $OutputDir -File
Write-Host "`n[*] Total exports in $OutputDir`: $($allExports.Count) file(s)" -ForegroundColor Yellow
Write-Host " Run against more servers, or package with:" -ForegroundColor DarkGray
Write-Host " .\Export-RVTools.ps1 -Zip" -ForegroundColor White
Write-Host ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment