Getting started with PowerCLI automation scripts

Running individual PowerCLI commands interactively is useful for quick checks, but the real value of PowerCLI is in automation: scripts that run on a schedule, generate reports, or apply changes across dozens of VMs without manual intervention.

This guide covers the core patterns you need to write practical PowerCLI scripts: connecting and disconnecting cleanly, handling credentials, iterating over VMs, filtering results, exporting data, and structuring scripts for reuse.

Prerequisites

Script structure

A well-structured PowerCLI script follows a consistent pattern regardless of what it does:

# 1. Define parameters at the top
param(
    [string]$VCenterServer = "vc01.vsphere.local",
    [string]$ReportPath    = "C:\Reports\vm-report.csv"
)

# 2. Connect to vCenter
Connect-VIServer -Server $VCenterServer

# 3. Do the work
# ... your logic here ...

# 4. Disconnect when done
Disconnect-VIServer -Server * -Confirm:$false

Keeping this structure consistent makes scripts easier to read, test, and hand off to other administrators.

Handling credentials safely

Avoid hardcoding passwords in scripts. Two common approaches work well in practice.

Interactive prompt — suitable for scripts run manually:

# Prompt the operator for credentials at runtime
$cred = Get-Credential
Connect-VIServer -Server vc01.vsphere.local -Credential $cred

Encrypted credential file — suitable for scheduled tasks on a trusted machine:

# Save credentials once (run this interactively, not in the script)
$cred = Get-Credential
$cred.Password | ConvertFrom-SecureString | Set-Content "C:\Scripts\vcenter-pass.txt"

# In the script, load credentials from the encrypted file
$user     = "administrator@vsphere.local"
$passFile = "C:\Scripts\vcenter-pass.txt"
$pass     = Get-Content $passFile | ConvertTo-SecureString
$cred     = New-Object System.Management.Automation.PSCredential($user, $pass)
Connect-VIServer -Server vc01.vsphere.local -Credential $cred
Warning: The encrypted credential file is tied to the Windows user account and machine that created it. It cannot be decrypted on a different machine or under a different user account. Store the file in a location only the service account running the script can access.

Iterating over VMs

Most PowerCLI scripts loop over a set of VMs and perform an action on each. The pipeline handles this naturally:

# Get all powered-on VMs and print their name and host
Get-VM | Where-Object { $_.PowerState -eq "PoweredOn" } | ForEach-Object {
    Write-Host "$($_.Name) is running on $($_.VMHost)"
}

To target a specific folder, cluster, or datacenter:

# Get VMs from a specific folder
Get-VM -Location "Production"

# Get VMs from a specific cluster
Get-Cluster "CL-PROD-01" | Get-VM

# Get VMs from a specific host
Get-VMHost "esxi01.vsphere.local" | Get-VM

Filtering and selecting properties

PowerCLI objects expose many properties. Use Select-Object to control which fields appear in output or exports:

# List VM name, power state, number of CPUs, and memory
Get-VM | Select-Object Name, PowerState, NumCpu, MemoryGB | Format-Table -AutoSize

# Filter VMs with more than 8 GB of memory
Get-VM | Where-Object { $_.MemoryGB -gt 8 } | Select-Object Name, MemoryGB

Exporting results to CSV

Exporting data to CSV is useful for inventory reports, capacity planning, and change records:

# Export a VM inventory report to CSV
Get-VM | Select-Object Name, PowerState, NumCpu, MemoryGB, @{N="Host"; E={$_.VMHost}} |
    Export-Csv -Path "C:\Reports\vm-inventory.csv" -NoTypeInformation

Write-Host "Report saved to C:\Reports\vm-inventory.csv"
Note: -NoTypeInformation removes the type header line that PowerShell adds by default to CSV files. Without it, the first row of the CSV contains a PowerShell object type string instead of column headers, which breaks most spreadsheet imports.

Error handling

Scripts that run unattended need to handle errors gracefully rather than stopping silently mid-run:

# Use try/catch to handle connection errors
try {
    Connect-VIServer -Server vc01.vsphere.local -ErrorAction Stop
    Write-Host "Connected successfully"
}
catch {
    Write-Host "Failed to connect: $_"
    exit 1
}

# Use -ErrorAction SilentlyContinue for non-critical operations
$snap = Get-Snapshot -VM "SRV-APP-01" -ErrorAction SilentlyContinue
if ($null -eq $snap) {
    Write-Host "No snapshots found for SRV-APP-01"
}

A complete example: VM inventory report

This script connects to vCenter, collects a VM inventory, exports it to CSV, and disconnects cleanly:

# vm-inventory.ps1
# Connects to vCenter, exports VM inventory to CSV, disconnects.
# Usage: .\vm-inventory.ps1 -VCenterServer vc01.vsphere.local -ReportPath C:\Reports\vms.csv

param(
    [string]$VCenterServer = "vc01.vsphere.local",
    [string]$ReportPath    = "C:\Reports\vm-inventory.csv"
)

# Connect - prompt for credentials at runtime
try {
    Connect-VIServer -Server $VCenterServer -ErrorAction Stop
}
catch {
    Write-Host "Connection failed: $_"
    exit 1
}

# Collect VM data with a calculated Host column
$report = Get-VM | Select-Object Name, PowerState, NumCpu, MemoryGB,
    @{Name="Host"; Expression={$_.VMHost.Name}},
    @{Name="GuestOS"; Expression={$_.Guest.OSFullName}}

# Export to CSV
$report | Export-Csv -Path $ReportPath -NoTypeInformation
Write-Host "Exported $($report.Count) VMs to $ReportPath"

# Disconnect cleanly
Disconnect-VIServer -Server * -Confirm:$false

Official reference

VMware PowerCLI — Broadcom Developer Portal

Related guides