Introduction
Managing hundreds, or thousands, of VMs requires automation that is repeatable, predictable, and safe. PowerCLI allows you to apply changes across the fleet with confidence, reducing risk and saving time.
In this article, you’ll learn how to:
- Power VMs on/off in bulk
- Take and remove snapshots programmatically
- Tag or annotate multiple VMs
- Perform mass storage migrations
- Automate shutdown/startup in order
- Troubleshoot common automation issues
My Personal Repository on GitHub
Bulk Power Operations
Power Off All VMs in a Folder
$folder = Get-Folder -Name "Dev"
Get-VM -Location $folder | Stop-VM -Confirm:$false
Power On All VMs on a Host
Get-VMHost -Name "esxi03.lab.local" | Get-VM | Start-VM
Power Off VMs Gracefully by OS Type
Get-VM | Where-Object {$_.Guest.OSFullName -like "*Windows*"} | Stop-VMGuest -Confirm:$false
Snapshot Automation
Take Snapshot for All Running VMs
Get-VM | Where-Object {$_.PowerState -eq "PoweredOn"} | ForEach-Object {
New-Snapshot -VM $_ -Name "PreChange-$(Get-Date -Format yyyyMMdd-HHmm)" -Description "Snapshot before updates"
}
Remove Snapshots Older Than 7 Days
Get-VM | Get-Snapshot | Where-Object {$_.Created -lt (Get-Date).AddDays(-7)} | Remove-Snapshot -Confirm:$false
Tagging and Annotations
Assign Tag to All VMs in a Resource Pool
$pool = Get-ResourcePool -Name "Production"
$tag = Get-Tag -Name "Backup-Enabled"
Get-VM -Location $pool | New-TagAssignment -Tag $tag
Bulk Update VM Notes
Get-VM | Where-Object {$_.Name -like "*App*"} | Set-VM -Notes "Application server - reviewed 2025-07"
Storage vMotion: Moving VMs Between Datastores
Move all powered-off VMs from one datastore to another.
$source = Get-Datastore -Name "DS1"
$dest = Get-Datastore -Name "DS2"
Get-VM | Where-Object {
$_.PowerState -eq "PoweredOff" -and
($_.ExtensionData.Config.DatastoreUrl -match $source.Name)
} | Move-VM -Datastore $dest
Use with caution: Validate I/O impact and available space.
Custom Ordered Shutdown / Startup Sequences
Power off based on naming or application tier.
$orderedList = "DB-", "App-", "Web-"
foreach ($prefix in $orderedList) {
Get-VM | Where-Object {$_.Name -like "$prefix*"} | Stop-VMGuest -Confirm:$false
Start-Sleep -Seconds 10
}
Reverse the list for startup logic.
Diagram: Bulk VM Lifecycle Management

Error Handling & Common Issues
| Scenario | Fix |
|---|---|
| Snapshot creation fails on encrypted VMs | Check encryption policy and VM version compatibility |
Stop-VMGuest hangs | Ensure VMware Tools is running; fallback to Stop-VM if needed |
| Storage move fails due to lock | Verify VM is powered off and not in backup state |
| Tag not found or duplicated | Use Get-Tag -Name 'TagName' and validate tag category assignment |
Use Case Example: Pre-Maintenance VM Handling
Before host patching:
- Take snapshot of critical VMs
- Gracefully shut down based on tier
- Migrate VMs to other hosts
PowerCLI snippet:
$criticalVMs = Get-VM | Where-Object {$_.Name -match "SQL|Exchange"}
$criticalVMs | New-Snapshot -Name "PrePatch" -Description "Pre-maintenance snapshot"
$criticalVMs | Stop-VMGuest -Confirm:$false
What’s Next
In the next article, we’ll cover:
- Automated VM deployment using templates
- Customizing guest OS with PowerCLI
- Deploying VMs from JSON/CSV input