Find and unmount CD drives with PowerCLI

Sometimes someone forgets to unmount the CD drive of a VM. This will interfere with operation tasks like setting a host in maintenance mode or using Update Manager.

I recently had this issue, and the “update manager compliance check” told me that several VM’s where connected to a CD drive, which may interrupt the update process. I quickly found my favorite tool for vcenter management, the powerCLI. The quick and dirty oneliner ended up like this:

get-cluster %clustername% | get-vm | where {(($_ | Get-CDDrive).isopath) -gt ""} | %{$_ | Get-CDDrive | Set-CDDrive -NoMedia -Confirm:$false}

Replace %clustername% with your cluster name, and all your troubles will be over šŸ™‚

I have always loved oneliners. I have spent the part time of my time working in bash terminals, where the history has always been persistent. Luckily Microsoft has also seen the light, and history is now persistent (and searchable) in powershell. Search for a phrase using ctrl+r, search further back by pressing ctrl+r again. I’m sure you will learn to love it, if you don’t already do.

Balance your VMs across ESXi hosts in a vSphere cluster with local storage

The Citrix guys in my company has a bad habbit of using local storage, probably because they have had some bad performance experiences in the past. Local storage is not good for the vSphere admin, as vMotion is set out of play, and I need to move things around manually when doing maintenance. Furthermore the Citrix provisioning tool is not very good at launching/deploying VM’s on hosts that has a lot of free resources, so pretty often we end up with clusters where 50% of the hosts is utilizing 90% of the resources (mainly memory) and 50 % is doing nothing šŸ™

To mitigate this I have written a script to “balance” the VM’s equally across the clusters. The script takes a parameter with the cluster name, and you are able to exclude specific hosts by editing the file.

# Script to balance VMs across ESXi hosts in cluster using local storage.
# Created by kasper@nordal-lund.dk
# Execute with cluster name as parameter
param(
[string]$cluster
)

#Make sure the vmware modules are loaded
Get-Module -name vmware* -ListAvailable | Import-Module

#Connect to the viserver
connect-viserver hostname.vcenter -alllinked -Credential (Get-Credential)

#$cluster = "xxx" # Manually define the cluster value. For testing purposes.

# Exclude hosts from the operation, use * as wildcard and seperate with |
$excluded = "" 

#Check if the cluster parameter is set.
if ($cluster -eq "") {
    write-host "You forgot to specify a cluster, please try again..."
    exit
}

# Fire up the main loop
while ($true) {

# Pull out the target hosts
$hosttargetsRaw =  get-cluster $cluster | get-vmhost | where {($_.connectionstate -eq "Connected")} | select name,@{N="VMCount";E={(get-vm -location $_.name).count}} | Sort-Object VMCount -Descending

# Trim the list for exclusions 
$hosttargetstrimmed = $hosttargetsRaw -notmatch $excluded

# Calculate the difference between the most and least populated hosts
$diff = ($hosttargetstrimmed | select -First 1).VMCount - ($hosttargetstrimmed | select -Last 1).VMCount

if ($diff -gt 4) {

# Define the source and destination hosts
$sourcehosts = $hosttargetstrimmed | select -First 2
$desthosts = $hosttargetstrimmed | select -last 2

# Check if we have only one hosts doing nothing
$bottomdiff = ($desthosts | select -first 1).VMcount - ($desthosts | select -Last 1).VMCount

$i = 0

foreach ($sourcehost in $sourcehosts.name){
	if ($bottomdiff -gt 4) {
        $curdesthost = $desthosts[1].name
    }else {
        $curdesthost = $desthosts[$i].name
	    $i++
    }
# Get the destination datastore		
$destds = (get-datastore -vmhost $curdesthost local*).name

# get the VM's we want to move
$targets = get-vmhost $sourcehost | get-vm | select -first 2

# Move the VM's 2 from each hosts = 4 VM's pr. loop
foreach ($target in $targets) {
    echo "move-vm -vm $target -Destination $curdesthost -Datastore $destds -RunAsync"
   }
}
# Wait for the VM's to be moved before looping again.  
sleep -Seconds 90
}else {
    write-host "Balancing of cluster $cluster is finished, difference between most and least populated host is $diff VMs..."
exit
}
}

I hope you are able to use this, at least as inspiration for getting on with your own.
And remember, this script can of course be combined with my powershell menu script found here: https://www.nordal-lund.dk/?p=574

Enjoy…

Add new virtualportgroup to vSwitch on multiple VMHosts with Powercli

If you dont have dvSwitches (Distributed vSwitches) in your vSphere cluster, or dont even have a cluster, you may have to add new portgroups manually, depending on the number of VMHosts this can be a pretty cumbersome task.

Luckily, we can use powercli to automate the task. In the following example i will use the -location parameter to define my tagets in the variable $hosts

Lets go ahead and define the VMhosts we want to target:

$hosts=get-vmhost -location “location”

Then, we create the new portgroup for every VMhost in the location by using a foreach loop:

foreach ($vmhost in $hosts) {Get-VMHost $vmhost | Get-VirtualSwitch -name “vSwitch0” | New-VirtualPortGroup -VLanId “10” -Name “Name”}

This will create a new VirtualPortGroup with VLAN ID 10 named “Name” on vSwitch0 on all the VMHosts in the specified location.

Remove the port again by doing this:

foreach ($vmhost in $hosts) {Get-VMHost $vmhost | Get-VirtualSwitch -name “vSwitch0” | Get-VirtualPortGroup -Name “Name” | Remove-VirtualPortGroup -Confirm:$false}

Remember that -Confirm:$false will remove the port without confirmation! Make sure you have the right targets in the $hosts variable!!!