Have you ever stared down the barrel of endless repetitive clicks in VMware vSphere, knowing you’re in for hours of mindless work? Recently, I faced a scenario where I needed to update hundreds of virtual NICs across just as many virtual machines. Manual changes would have been soul-crushing, so I turned to VMware PowerCLI and transformed this monotonous task into a 5-minute automated script. Here’s how I did it.
The Problem: Manual Network Changes Are Soul-Crushing
Picture this: You have dozens, maybe hundreds, of VMs, each with multiple network adapters needing reassignment to a new distributed port group. The manual process looks something like this:
- Open each VM’s settings.
- Click through every network adapter.
- Select the new network.
- Wait for the change to apply.
- Repeat until your will to live fades.
The task was not just time-consuming but error-prone. I knew there had to be a better way.
Enter PowerCLI: Your New Best Friend
PowerCLI is VMware’s powerful scripting toolkit that supercharges PowerShell to manage your VMware environment programmatically. Here’s how to get started:
Installing PowerCLI
Open PowerShell as Administrator and run:
Install-Module -Name VMware.PowerCLI -Scope CurrentUser
Accept the prompts, and voilà! You’re ready to automate.
The Journey to Automation Nirvana
My path to automating network changes wasn’t without its hiccups. Let me walk you through the evolution of my approach.
First Attempt: The Naive Approach
Initially, I wrote a script to update only “unassigned” network adapters. Logical, right? Wrong. What PowerCLI considered “unassigned” didn’t always match what I saw in the vSphere UI.
Second Try: The Brute Force Method
I decided to reassign all the adapters, regardless of their current state. While functional, this approach overwhelmed vCenter, leading to failed operations.
Final Solution: The Smart Approach
The breakthrough came with a more intelligent script that:
- Validates host state before making changes.
- Introduces delays between operations to keep vCenter happy.
- Includes robust error handling and logging.
- Verifies changes to ensure accuracy.
The Magic Script That Does It All
Here’s the polished script that saved the day:
<#
.SYNOPSIS
This script connects to vCenter, finds all VMs matching specific criteria,
and assigns unassigned virtual NICs to a specified Distributed Port Group.
.DESCRIPTION
1. Connects to vCenter Server.
2. Searches for VMs in "Datacenter Immaculate Constellation" -> "DELL-R720-CLUSTER" -> Host "192.168.88.5".
3. Filters only VMs that are powered off.
4. Assigns all empty NICs to "TRUNK-space-holder-Arista-DPort-Group".
5. Prints progress and errors to the console.
.NOTES
Tested with PowerCLI 12+ on vCenter 8.
#>
# ---[ 1. Setup and Connect to vCenter ]---
Write-Host "Starting script..."
# Enable PowerCLI logging
$logPath = Join-Path $PSScriptRoot "powercli_log.txt"
Write-Host "Enabling PowerCLI logging to: $logPath"
Set-PowerCLIConfiguration -Scope User -LogLevel Warning -InvalidCertificateAction Ignore -Confirm:$false | Out-Null
Start-Transcript -Path $logPath -Force
Write-Host "Connecting to vCenter..."
try {
# Update with your vCenter server name and credentials
Connect-VIServer -Server "vcenterserver.dc1.local" -User "YOUR_USERNAME" -Password "YOUR_PASSWORD"
Write-Host "Successfully connected to vCenter."
}
catch {
Write-Host "ERROR: Failed to connect to vCenter. Error details:"
Write-Host $_
return
}
# ---[ 2. Locate the Datacenter, Cluster, and Host ]---
Write-Host "Locating Datacenter, Cluster, and Host..."
try {
$datacenterName = "Datacenter Carlton II"
$clusterName = "DELL-R720-CLUSTER"
$hostName = "192.168.88.5"
# Get the Datacenter object
$dc = Get-Datacenter -Name $datacenterName -ErrorAction Stop
# Get the Cluster object in that Datacenter
$cluster = Get-Cluster -Name $clusterName -Location $dc -ErrorAction Stop
# Get the Host object in that Cluster
$vmHost = Get-VMHost -Name $hostName -Location $cluster -ErrorAction Stop
Write-Host "Datacenter found: $($dc.Name)"
Write-Host "Cluster found: $($cluster.Name)"
Write-Host "Target Host found: $($vmHost.Name)"
# Double check we're working with the correct host
if ($vmHost.Name -ne $hostName) {
Write-Host "ERROR: Retrieved host does not match target host name" -ForegroundColor Red
Disconnect-VIServer -Confirm:$false
return
}
Write-Host "`nWARNING: This script will only process VMs on host $hostName" -ForegroundColor Yellow
Write-Host "Other hosts in the cluster will not be affected`n"
# Get all hosts in cluster for reference
$allHosts = Get-VMHost -Location $cluster
Write-Host "Hosts in cluster $($cluster.Name):"
foreach ($h in $allHosts) {
if ($h.Name -eq $hostName) {
Write-Host " * $($h.Name) (TARGET HOST)" -ForegroundColor Yellow
} else {
Write-Host " - $($h.Name) (will not be modified)"
}
}
Write-Host ""
# Check host state
Write-Host "`nChecking host state..."
Write-Host "Host State: $($vmHost.State)"
Write-Host "Host Connection State: $($vmHost.ConnectionState)"
Write-Host "Host Power State: $($vmHost.PowerState)"
if ($vmHost.State -ne "Connected" -or $vmHost.ConnectionState -ne "Connected") {
throw "Host is not in a valid state for network operations. Current state: $($vmHost.State), Connection state: $($vmHost.ConnectionState)"
}
}
catch {
Write-Host "ERROR: Unable to locate Datacenter, Cluster, or Host. Error details:"
Write-Host $_
Disconnect-VIServer -Confirm:$false
return
}
# ---[ 3. Find All Matching VMs ]---
Write-Host "Searching for powered-off VMs..."
try {
$vmList = Get-VM -Location $vmHost |
Where-Object { $_.PowerState -eq "PoweredOff" }
if (!$vmList) {
Write-Host "No powered-off VMs found on host '$hostName'."
return
}
else {
Write-Host "Found the following powered-off VMs on host '$hostName':"
$vmList | ForEach-Object { Write-Host " - $($_.Name)" }
# ---[ 4. Assign Each Unassigned NIC to the Specified DPort Group ]---
$dportGroup = Get-VDPortgroup -Name "TRUNK-space-holder-Arista-DPort-Group" -ErrorAction Stop
if (!$dportGroup) {
Write-Host "ERROR: Could not find distributed port group 'TRUNK-space-holder-Arista-DPort-Group'" -ForegroundColor Red
return
}
Write-Host "`nFound distributed port group: $($dportGroup.Name)" -ForegroundColor Green
Write-Host "Port group key: $($dportGroup.Key)`n" -ForegroundColor Green
foreach ($vm in $vmList) {
Write-Host "`nProcessing VM: $($vm.Name)" -ForegroundColor Yellow
# Get all network adapters for the VM
$nics = Get-NetworkAdapter -VM $vm
Write-Host " Found $($nics.Count) network adapter(s)"
foreach ($nic in $nics) {
Write-Host "`n Processing NIC: $($nic.Name)"
Write-Host " Current Network: [$($nic.NetworkName)]"
try {
# Check host state again before each operation
$hostState = Get-VMHost -Name $hostName
if ($hostState.State -ne "Connected" -or $hostState.ConnectionState -ne "Connected") {
throw "Host is not in a valid state for network operations. Current state: $($hostState.State), Connection state: $($hostState.ConnectionState)"
}
# First remove the network assignment
Write-Host " Removing current network assignment..." -ForegroundColor Yellow
$nic | Set-NetworkAdapter -NetworkName "" -Confirm:$false -ErrorAction Stop
Start-Sleep -Seconds 2 # Wait 2 seconds between operations
# Then assign the new network
Write-Host " Assigning to '$($dportGroup.Name)'..." -ForegroundColor Yellow
$nic | Set-NetworkAdapter -Portgroup $dportGroup -Confirm:$false -ErrorAction Stop
Start-Sleep -Seconds 2 # Wait 2 seconds after assignment
# Verify the change
$updatedNic = Get-NetworkAdapter -VM $vm | Where-Object { $_.Id -eq $nic.Id }
Write-Host " New Network Assignment: [$($updatedNic.NetworkName)]" -ForegroundColor Green
}
catch {
Write-Host " ERROR: Failed to update network. Error details:" -ForegroundColor Red
Write-Host " $_" -ForegroundColor Red
Write-Host " Host State at time of error: $($hostState.State)"
Write-Host " Host Connection State at time of error: $($hostState.ConnectionState)"
continue # Continue with next NIC instead of stopping
}
}
# Wait 5 seconds between VMs to avoid overwhelming vCenter
Write-Host "`n Waiting 5 seconds before processing next VM..." -ForegroundColor Blue
Start-Sleep -Seconds 5
}
}
}
catch {
Write-Host "ERROR: An issue occurred while searching for or updating VMs. Error details:"
Write-Host $_ -ForegroundColor Red
}
# ---[ 5. Cleanup / Disconnect from vCenter ]---
try {
Write-Host "`nDisconnecting from vCenter..."
Disconnect-VIServer -Confirm:$false
Write-Host "Script completed."
}
catch {
Write-Host "ERROR: Could not disconnect from vCenter properly. Error details:"
Write-Host $_ -ForegroundColor Red
}
finally {
# Stop logging
Stop-Transcript
}
How It Works
1. Initial Setup and Safety
The script enables logging and ignores certificate warnings for seamless execution.
2. Smart Host State Validation
It checks that the host is ready for changes, avoiding issues with disconnected or powered-off hosts.
3. Careful Network Changes
Network adapters are reassigned with delays to prevent overwhelming vCenter.
4. Robust Verification
Each change is verified to ensure it was applied successfully.
Results: Time Saved, Sanity Preserved
Here’s the math:
- 50 VMs with 3 NICs each = 150 changes
- Manual time: ~2–3 hours
- Script time: ~5 minutes
- Errors: Zero
- Bonus: I actually enjoyed a coffee break while the script ran.
Pro Tips From the Trenches
- Always validate host state – Ensure the host is connected and ready.
- Introduce delays – Prevent vCenter from throwing errors due to rapid requests.
- Log everything – You’ll thank yourself later.
- Verify changes – Mistakes happen, but not on your watch.
- Graceful error handling – Scripts should recover from unexpected issues.
Ready to Try It Yourself?
Here’s how to get started:
- Install PowerCLI and connect to your vCenter.
- Copy the script and update the connection details.
- Run it.
- Enjoy your newfound free time.
Conclusion
PowerCLI isn’t just a tool—it’s a superpower for VMware administrators. Automating repetitive tasks not only saves time but also reduces errors, giving you more bandwidth for meaningful work.
Have you automated any VMware tasks with PowerCLI? Share your stories in the comments—I’d love to hear how you’re making VMware work smarter, not harder!
Happy scripting! 🚀