How to Restart, Reset, Shutdown, Poweroff VMs with PowerCLI

It’s been a while… been a while since I blogged and I’m ashamed to say been a while since I scripted! But there is nothing like a crisis to bring out the scripting guns when we had to restart a 100 odd virtual machines which were hung. Which made me realize that while I had the script already made, I hadn’t made a post about it. So here it is, and hope it would be useful for you as it was for me!

In all of these commands I will use -Confirm:$False at the end. Confirm:$false is used to disregard the Confirmation prompt that PowerCLI gives out. It’s an optional parameter that is useful if you’re executing this for bulk VMs

Restart VMs with PowerCLI

The command to gracefully restart a VM from the Guest OS – which is to say to send a restart command to the guest operating system and let it do its thing – is the following :

Restart-VMGuest -VM <VM_name> -Confirm:$False

This command is useful if you prefer to let the OS gracefully restart itself after, say, a memory or CPU upgrade.

*VM Tools is required for this command to work because the vCenter API has to communicate with the OS for the command to execute.

It is the same as pressing the “Restart Guest OS” button on the vCenter

Reset VMs with PowerCLI

The command to reset a VM – which is pretty much like pressing the physical reset button to restart the system – is the following :

Restart-VM -VM <VM_name> -Confirm:$False

This command is useful if the VM is hung or unresponsive and resetting it is the only option you have left.

It is the same as pressing the Reset button on the vCenter

Shutdown VMs with PowerCLI

The command to gracefully shutdown a VM from the Guest OS – which is to say to send a shutdown command to the guest operating system and let it do its thing – is the following :

Shutdown-VMGuest -VM <VM_name> -Confirm:$False

This command is useful if you prefer to let the OS gracefully shut itself down during, for example, a downtime.

*VM Tools is required for this command to work because the vCenter API has to communicate with the OS for the command to execute.

It’s the same as pressing the “Shut Down Guest OS” button on the vCenter

Power Off VMs with PowerCLI

The command to forcefully power off a VM – which is pretty much like yanking the power cable off a server (or less violently powering off by pressing the power button) – is the following :

Stop-VM -VM <VM_name> -Confirm:$False

This command is useful you want the VMs shut down as quickly as possibly or if its unresponsive.

It’s the same as pressing the “Power Off” button on the vCenter

Putting it all together to a script for executing on bulk VMs

The above commands are to execute on one VM, lets see how we can use them to create a full fledged script.

Long term followers of the blog will know that I like to put the list of VMs for the script to be executed on in a text file and import the list via the text file to the script – read all about it here. The reason I do this is that it’s easier to change the VM list later on which increases the reusability of the script.

Lets see how we can put together a script to reset a list of VMs which I assume is contained in a text file called “vmlist.txt”

$vm_list = Get-Content "vmlist.txt" #txt file with the vm list
$vcenter = "<vcenter_name>"


Connect-VIServer $vcenter 

foreach ($vm in $vm_list){

Restart-VM -VM <VM_name> -Confirm:$False #<replace this with any of the above commands to suit your need>
}

Disconnect-VIServer -Confirm:$false

To change between restart, reset, shutdown or power off – change the line Restart-VM… with the command you want.

That is it! As easy as that!

It makes me feel validated & boosts my ego if you share, comment if you found this useful (no pressure 😉  ). Social media links are below!

And as always, Happy scripting!

How to get the Operating System (OS) of Virtual machines with PowerCLI

There have been numerous occasions where we were required to gather the list of VMs in the vCenter along with their OS, for audits, for support, to plan patching, etc… But its not always that easy.

The vCenter gets the OS details of the VM with the aid of VM Tools in the virtual machine. But what if the VM is powered off or doesn’t have tools installed? Then the OS is left as blank! But there is a workaround, we can fetch the OS info the VM was configured with – since that information is stored in the vCenter itself, we don’t have to rely on the VM tools running inside the VM to get this information.

This is in the $_.ExtensionData.Config.GuestFullName parameter in Get-VM

One Liner to get the list of all VMs and OS info in the vCenter

Get-VM | Select @{Label = "VM Name" ; Expression = {$_.Name} },@{Label = "Guest OS" ; Expression = {$_.ExtensionData.Config.GuestFullName} }| Export-CSV "vm_os_info.csv"

Its as simple as that. You will get a csv with two columns as “VM Name” and “Guest OS” with all the information in the vCenter wrapped in a neat little bow.

Hope this short and sweet post was useful to you!

It makes me feel validated & helps me out a lot if you share or leave a comment if you found this useful (no pressure 😉 ). Social media links are below!

And as always, Happy scripting!

How to check EVC Status of a Host Cluster with PowerCLI

Consider this part 2 of my vSphere Health Check with PowerCLI series. It’s taken its sweet time, I know, but hey – better late than never eh? 😉 Part 1 is found here (Checking for hosts with alarms disabled) .

In this post we’ll discuss how to check the EVC mode of each host cluster in the vCenter. EVC (Enhanced vMotion Capability) is a feature that is a form of standardization of vMotion compatibility in hosts in the cluster according to CPU generation. It sets a minimum feature set offered by CPUs in the cluster so that all hosts presents identical features so that things don’t go haywire when VMs migrate between them. Anyway this is not a lesson on EVC (if you want more info, refer the KB article), this is a lesson on checking on the EVC mode on a cluster by PowerCLI

We will first check whether EVC is enabled in the first place, and if its enabled, which EVC mode its enabled to. While this seems like a lot of complexity – its actually only a few lines of code.

Checking if EVC is enabled

EVCmode information is encapsulated in the Get-Cluster module. So let’s access Get-Cluster and check on the EVCmode parameter. If this EVC mode returns a null value then EVC is not enabled in that cluster. If EVC mode is enabled then it would return the EVC Mode (like Intel “Haswell”, Intel “Skylake”, Intel “Cascade Lake” etc…). Lets see how it comes in to play

$cluster = $Get-Cluster <cluster_name>
    
if($null -eq $cluster.EVCMode){
        $evc_status = "Status : Disabled"
    }

else{
        $evc_mode = $cluster.EVCMode

        $evc_status = "Status : Enabled EVC Mode : $evc_mode"

    }

One-Liner

Lets see how we can use the above principle to get a one-liner to export a csv with the EVC mode results

Get-Cluster |  Select @{N='Cluster';E={$_.Name}},@{N='EVC Mode';E={if($null -eq $_.EVCMode){"EVC not enabled"} else{$_.EVCMode}}} | Export-Csv 'cluster_evc.csv'

And that’s it! Its as simple as that!

It makes me feel validated & helps me out a lot if you share, comment if you found this useful (no pressure 😉 ). Social media links are below!

And as always, Happy scripting!

How to enable ESXi host alarms on the vCenter with PowerCLI (& how to automate it)

In a previous write-up I discussed how to check the Alarm status of ESX hosts in a cluster. Let’s take it a step further and check how we can enable the alarms on an ESX host in order to automate it – so that alarms will be enabled on ESX hosts for eternity, as a healthy environment should! No longer shall we be in doubt whether we forgot to enable alarms in a ESX host after working on it! This is actually very simple! Lets get to it!

Here I will be talking about enabling alarms on all hosts in the vCenter (not on a particular cluster). You can put this script on a task scheduler to keep it running on a schedule so that Alarms will be enabled periodically throughout the day.

Step 1: Getting the list of ESX hosts whose alarms are disabled

$alarm_disabled_hosts = Get-VMHost | Where-Object {$_.extensiondata.AlarmActionsEnabled -eq $false}

This was discussed in the previous post, so I won’t discuss this at length. Its a pretty self evident line anyway 😉 With Get-VMHost called raw and bare naked like that we loop through all hosts piped to the where-object clause which checks for the AlarmActionsEnabled extension data.

Step 2: Enable them alarms!

The Service Instance in charge of handling alarms in the vCenter is the Alarm manager. So lets get a view of that manager entity to call on its management capabilities.

$alarmMgr = Get-View AlarmManager

Now that we have called on the Alarm Manager to the $alarmMgr variable, we can also call on EnableAlarmActions to enable alarms from our list of hosts whose alarms are disabled

$alarmMgr.EnableAlarmActions(<host_name>.Extensiondata.MoRef,$true)

Above line enables the alarms on the host that we specify

Let’s see how we can put all this together in one full script:

Connect-VIServer <vCenter>

$alarm_disabled_hosts = Get-VMHost | Where-Object {$_.extensiondata.AlarmActionsEnabled -eq $false}

$alarmMgr = Get-View AlarmManager


ForEach ($hosts in $alarm_disabled_hosts){

    $alarmMgr.EnableAlarmActions($hosts.Extensiondata.MoRef,$true)
    
}

This would enable alarms on all ESX hosts whose alarms are currently disabled.

Extra Credit : Skip hosts in maintenance mode

If the host is in maintenance mode, chances are that you are working on some error on the host and you will have to keep those alarms disabled. So for extra credit let’s see how we can modify the script to skip the hosts in maintenance mode:

Connect-VIServer <vCenter>

$alarmMgr = Get-View AlarmManager


ForEach ($hosts in $alarm_disabled_hosts){

    if ($hosts.ConnectionState -eq "Maintenance"){
        Write-Host "$hosts in maintenance mode, alarms will remain disabled"
    }

    else{
        Write-Host "Enabling alarms in $hosts"
        $alarmMgr.EnableAlarmActions($hosts.Extensiondata.MoRef,$true)
    }

}

And thats it! Keep this running on a schedule and you can be sure your environment has constant monitoring enabled.

If you found this useful, it helps me out if you share, comment or just generally spread the word (no pressure 😉 ). Social media links are below!

And as always, Happy scripting!

How to check ESX host version and build number with PowerCLI

Are you planning an ESXi upgrade? or in the middle of a cluster ESXi upgrade and couldn’t track which ones you upgraded and which ones you didn’t? (which is what happened to me 😉 )

So I made a script that would output the build number and version number of ESXi hosts in a cluster

The One-liner

If you’re in a pinch and you just need to check the status of hosts with minimal effort, you can use this one-liner

Get-Cluster "<cluster_name>" | Get-VMHost | Select @{Label = "Host"; Expression = {$_.Name}} , @{Label = "ESX Version"; Expression = {$_.version}}, @{Label = "ESX Build" ; Expression = {$_.build}}

Keep in mind that you need to be logged in to the vCenter with PowerCLI before executing this (obviously). This will ouput something like this:

Explanation:
The mechanism behind this is quite simple. Get-VMHost cmdlet contains parameters called “build” and “version” built in to it. What we are simply doing is outputting the values of those specific parameters.

Script to Output to a CSV

Need something fancier to share with your team or your superiors? Let’s see how we can expand from the one-liner to a proper script that is expandable and versatile that can output to a csv.

$vcenter = "<vcenter_name>"

$cluster_name = "<cluster_name>"

Connect-VIServer $vcenter 

Get-Cluster $cluster_name | Get-VMHost | Select @{Label = "Host"; Expression = {$_.Name}} , @{Label = "ESX Version"; Expression = {$_.version}}, @{Label = "ESX Build" ; Expression = {$_.build}} | Export-csv "cluster_host_build_info.csv"


Disconnect-VIServer -Confirm:$false

What’s different with the one-liner is the pipe to “Export-csv” which will create the CSV file. Please note that this CSV file will be created on the folder where the script is located.

We have also parameterized the vCenter name and Cluster name for better versatility. Pretty neat huh? What – you want more? Let’s see how we can expand this further to match a build level that you want.

Script to check whether a host is in the build level you want

I hear you… You’re in the middle of upgrading an ESX cluster and you want to check which hosts are left to upgrade – while you can judge for yourself from the above script, here’s a small addition to our script above that makes our lives muuuuch easier

$vcenter = "<vCenter_name>"

$cluster_name = "<cluster_name>"
$expected_build = <expected_build_number>

Connect-VIServer $vcenter 

Get-Cluster $cluster_name | Get-VMHost | Select @{Label = "Host"; Expression = {$_.Name}} , @{Label = "ESX Version"; Expression = {$_.version}}, @{Label = "ESX Build" ; Expression = {$_.build}} , @{Label = "Build Match" ; Expression = {if($_.build -eq $expected_build){"Yes"} else{"No"}}} | Export-csv "cluster_host_build_info.csv"


Disconnect-VIServer -Confirm:$false

The only difference is this line

@{Label = "Build Match" ; Expression = {if($_.build -eq $expected_build){"Yes"} else{"No"}}

Where we check with an IF condition whether the ESX host’s build matches our expected build version that we defined in $expected_build variable above. It will return a “Yes” or a “No”.

In the example below $expected_build = 15169789 and the output will be similar to the screenshot

And that is it! Hope this was as useful to you as it was for me!

It makes me feel validated & boosts my ego if you share, comment if you found this useful (no pressure 😉 ). Social media links are below!

And as always, Happy scripting!

Enable and Disable ESXi Host SSH with PowerCLI | VMHostService Cmdlet

Maybe you’re upgrading your hosts, maybe you’re trying to run a command on multiple ESXi Hosts, any which way one thing which you will have to do is enable SSH from the vCenter, and disable when you’re done – because annoying as it is, security is important! 😉 It would be especially annoying if you have to do that for dozens of ESXi hosts at a time.

So here I’ll show you guys a simple script you can run to enable and then disable SSH on multiple ESXi hosts so that you can go ahead and do your main task instead of being distracted by side quests!

Enable SSH on ESX host

$host_list = Get-Content "host_list.txt"


Connect-VIServer $vcenter 


foreach($hosts in $host_list){
	Get-VMHostService -VMHost $hosts | Where-Object {$_.Key -eq "TSM-SSH" } | Start-VMHostService -confirm:$false 
}


Disconnect-VIServer * -confirm:$false 

If you’ve followed the blog you will know I like to follow the Get-Content method to get a list of things (in this case list of ESXi Hosts) from a simple text file (separated by new lines). You can follow how to use Get-Content here

The real star of the show is this guy right here:

Get-VMHostService -VMHost $hosts | Where-Object {$_.Key -eq "TSM-SSH" } | Start-VMHostService -confirm:$false 

We use the Get-VMHostService cmdlet to output the list of services that is available on the ESX host. From there we pipe that list and search for “TSM-SSH” service. Once that is filtered we pipe that service over to the Start-VMHostService cmdlet which will enable SSH on the host. This usually triggers a “Are you sure?” on the shell but since we need things as automated as possible we disable that with -confirm:$false

That’s it! Simples!

Disable SSH on ESX host

$host_list = Get-Content "host_list.txt"

Connect-VIServer $vcenter 


foreach($hosts in $host_list){
	Get-VMHostService -VMHost $hosts | Where-Object {$_.Key -eq "TSM-SSH" } | Stop-VMHostService -confirm:$false 
}


Disconnect-VIServer * -confirm:$false 

This is exactly the same as above except that instead of piping the filtered SSH service to “Start-VMHostService” cmdlet, we pipe it to “Stop-VMHostService” cmdlet. Self explanatory really (works out because I’m lazy to type at this point xD)

And that is it! It makes me feel validated if you like, share, comment if you found this useful (no pressure 😉 )! Social Media Links are below

Happy Scripting!

Getting Snapshot details of Virtual Machines | Get-Snapshot Cmdlet | PowerCLI

Are you a vSphere Admin facing complaints of Virtual Machine slowness? Are rogue, stale and abandoned Snapshots infesting your infrastructure? Boy have I got the script for you!

Sorry about the “as seen on TV” intro, but as a vSphere admin sometimes snapshots do get out of hand. Below is a simple little one-liner using Get-Snapshot Cmdlet to get a list of all snapshots in your environment.

The One-Liner

Get-VM | Get-Snapshot | Select @{Label = "VM"; Expression = {$_.VM}}, @{Label = "Snapshot Name";Expression = {$_.Name}}, @{Label = "Created Date"; Expression = {$_.Created}} , @{Label = "Snapshot Size"; Expression = {$_.SizeGB}} | Export-Csv "snapshot_details.csv"

Script breakdown:

The one-liner will output:

  • The virtual machine which has the snapshot
  • The name of the snapshot
  • The date the snapshot was created
  • The size of the snapshot

This one is fairly simple.

Get-VM when executed alone gets the list of all VMs of the vCenter, which is then piped to (executes like a foreach loop on the next command) the Get-Snapshot Cmdlet which gets all the information of a snapshot of that particular VM (like you specified it like this – Get-Snapshot -VM vm_name)

Then we simply select what we want to output. The Get-Snapshot Cmdlet fetches the following data:

To select, simply add a @{Label = “basically the column name”; Expression = {$_.theParameterThatYouWant}} block to the SELECT statement of the one-liner. You can add as many columns like this as you want separated by commas.

And that is it. In a future post maybe I will pursue how to select snapshots that are older than x number of days and things like that. But until that day comes, hope you enjoyed this one and found it to be useful!

Leave a comment, share and follow me on Social Media! Happy Scripting!

Getting the DNS Servers associated with an ESXi Host | PowerCLI | One-Liner

We recently had to check for discrepancies in configurations of ESXi hosts in a datacenter. While they should be identical, we have had issues with DNS working on some hosts while some don’t. So I developed a script that outputs the DNS servers configured on every host of the entire vCenter to a CSV file. (Note: This outputs the IPs of the DNS servers)

Its a one-liner so its not difficult to execute, check it out :

$vcenter = "vcenter_name"

Connect-VIServer $vcenter 

Get-VMHost | Select Name, @{N='DNS Server(s)';E={$_.Extensiondata.Config.Network.DnsConfig.Address -join ' , '}} | Export-Csv 'esx_dns_info.csv' 

Code breakdown:

Basically what we are doing is getting the data that is hidden inside the data that is pulled by the Get-VMHost module -in this case it’s under extension data/config/Network/DNSConfig/Address – which fetches the IP’s of these servers.

Why I used the -join ‘,’ is that a single ESXi host may have multiple DNS servers configured for redundancy, but this will not be outputted properly with CSV. So what I’m doing is joining the list of IPs to a single line separated by a comma.

Then finally pipe all that data in to the Export-CSV module that will export it to a CSV file I’ve named “esx_dns_info.csv”

And that is it – simple as that. Hope that was a easy and clear guide to getting the DNS servers associated with an ESX host.

If you liked it, please share the news! Happy Scripting!

Get the ISO Path of mounted CDs on VMs | Get-CDDrive Cmdlet

Has it happened to you that you see VMs that do not have any associated disks in certain datastores show up in those datastores, also not allowing vMotions as well leaving you confused? Well it could be that there are ISOs in that datastore mounted to those VMs! This is tedious when you are doing datastore cleanups and what not. In this post we’ll get to the bottom of creating a script that will generate a list of VMs with ISOs mounted and their paths so that you can deal with them as you wish.

There are two ways to get this, the lazy one-liner way and the a more comprehensive script. Both versions use the Get-CDDrive Cmdlet to pull the CD drive information of a VM. Lets discuss both shall we :

The lazy One-Liner

This one liner will output a csv containing ISO paths of all the VMs in the vCenter (even on the VMs without an ISO mounted to them). If you don’t mind that – this is the easiest way to get this done.

Get-VM | Select Name, @{Label="ISO file"; Expression = { ($_ | Get-CDDrive).ISOPath }} | Export-Csv "vms_with_isos.csv"

This is fine for a small vCenter with a few VMs, but is very inconvenient for a large vCenter when the output file is a few thousand lines long. So lets look at a script that will only get the VMs with ISOs mounted to them.

The Comprehensive script

$vcenter = "vcenter_name"

$vm_list_with_iso = @()

Connect-VIServer $vcenter 

$vmlist = Get-VM 

foreach($vm in $vmlist){
	$cdinfo = Get-CDDrive $vm
	
	if($cdinfo.IsoPath -ne $null){
		
		$details = @{
			VM_name = $vm.name
			ISOPath = $cdinfo.IsoPath

		} 

		$vm_list_with_iso += New-Object PSObject -Property $details | Select VM_name,ISOPath

	}

}

$vm_list_with_iso | Export-Csv "vms_with_isos.csv"

Even though the script seems bigger, whats different is the “if” statement where it checks whether the ISOPath output by the Get-CDDrive Cmdlet is equal to $null (null variable used by powershell) or not. If its not null then that means an ISO is mounted.

And that is it!

If you liked it, please share the news! Happy Scripting!

Changing the vCenter Alarm reporting frequency | PowerCLI

Recently we noticed that in vCenters while we receive SNMP or Email (SMTP) red alerts, we did not receive the green alerts that changed immediately.

For example if an ESX host had a memory utilization of 95%, it would trigger a red state alarm and would send out an email or SNMP trap notifying the admins “HELP SOMETHING IS WRONG“, but should that alarm flap immediately and return to green (below that 95% or 90% whichever is set) – the vCenter doesn’t send out an email or SNMP saying “okay false alarm calm down“. This would waste a vigilant system administrator’s time checking an alarm that was a fluke.

According to this VMware KB article the alarm actions like Email alerts/SNMP traps are not triggered for state changes within 5 minutes from initial alarm trigger. So it’s official. And there is no GUI way to change this, it’s all PowerCLI to the rescue.

We didn’t notice this until we built an application that shows real time vCenter alarms which was sensitive to flapping state changes. So we had to take action to set the alarming frequency from 5 minutes to 0 to get immediate alarms. Fortunately you can do this for individual alarms, which means its not a global setting that changes the alarm frequency of all alarms in the vCenter.

Enough chit chat – down to scripting! I will do a breakdown of the full script to explain what I’m doing and have the full script down below, if you’re lazy you can just copy the script and run it

#Credentials 
$user = "vcenter_username"
$pwd = "vcenter_password"

#the vcenter name goes here
$server = "vcenter_1"
	
Connect-VIServer $server -User $user -Password $pwd

#variable with the name of the alarm that you want to change the alarming frequency of
$alarm_check = "vCenter host alarm"
write-host "checking alarm $alarm_check"
	
$alarm_id = (Get-AlarmDefinition | where name -eq $alarm_check ).id
$alarm_view = Get-View -id $alarm_id

echo $alarm_view.info.setting

#create a spec variable that holds a copy of data to update
$spec_to_update = $alarm_view.info
$spec_to_update.Setting.ReportingFrequency = 0

Write-Host "Reconfiguring $alarm_check"
$alarm_view.ReconfigureAlarm($spec_to_update)

Write-Host "New Reporting frequency of $alarm_check"

$new_alarm_id = (Get-AlarmDefinition | where name -eq $alarm_check ).id
$new_alarm_view = Get-View -id $new_alarm_id

echo $new_alarm_view.info.setting

For those of you who want to know what just happened with this script here is a breakdown of the script:
First up we need to connect to the vCenter via Connect-VIServer

#Credentials 
$user = "vcenter_username"
$pwd = "vcenter_password"

#the vcenter name goes here
$server = "vcenter_1"
	
Connect-VIServer $server -User $user -Password $pwd

Next up we create a variable that has the name of the alarm which you want to change the reporting frequency of, we’re calling that $alarm_check. (I’m taking the vcenter host alarm example I took above for this too and assuming the alarm name is “vCenter host alarm”)

$alarm_check = "vCenter host alarm"
write-host "checking alarm $alarm_check"

Then we take the ID of the alarm (vCenter has a separate ID for every alarm to identify every alarm uniquely), then we get-view that alarm ID which shows all the settings of that alarm. We then echo the settings to see whether the reporting frequency is actually 5 minutes

$alarm_id = (Get-AlarmDefinition | where name -eq $alarm_check ).id
$alarm_view = Get-View -id $alarm_id

echo $alarm_view.info.setting

Here’s where things might get a liiiiiittle bit confusing. We create a copy of that alarm-view data, and then create a proxy copy of data that need to be there with the new alarm reporting frequency of 0. Basically we have created a variable with the setting that we want to be there..

#create a spec variable that holds a copy of data to update
$spec_to_update = $alarm_view.info
$spec_to_update.Setting.ReportingFrequency = 0

Then we take the original alarm-view and reconfigure the original alarm with the proxy copy of data that we created with the data that we need to be there

Write-Host "Reconfiguring $alarm_check"
$alarm_view.ReconfigureAlarm($spec_to_update)

Like the good Sys Admins that we are – then we double check whether the change did take place

$new_alarm_id = (Get-AlarmDefinition | where name -eq $alarm_check ).id
$new_alarm_view = Get-View -id $new_alarm_id

echo $new_alarm_view.info.setting
The output should look a little something like this

And that’s it! The vCenter should send alarm state changes immediately now!

The main issue is there is no GUI way of doing this, you have to rely on PowerCLI to do that for you. But if you do find a better, lazier way to do this please do let me know – I mean that’s why we script right? because we laaaaazy 😉