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 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!

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!

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 😉

Expanding an existing disk on a Virtual Machine with PowerCLI | Get-HardDisk & Set-HardDisk cmdlets

We recently had a sudden requirement to increase the C: drives of a set of VMs to assist with an Operating system patching process – its not practical to to manually do that one by one – so PowerCLI to the rescue. So if you get a scenario where you need to expand an existing disk of a virtual machine by a specified amount of GB (especially a bulk of VMs), the following script will make life much easier.

Get the current size of the required disk

Use the code below to get the size (in GB) of the harddisk in question (here I’m getting the size of “hard disk 1”)

$vm_info_dsk = (Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq "Hard Disk 1"}).CapacityGB

You can make this a more expandable/usable code by making the Hard disk number a variable as well. With this you can change the name of the Hard disk easily without having to replace all the instances where the name was used.

$hard_disk_number = "Hard Disk 1"

$vm_info_dsk = (Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq $hard_disk_number}).CapacityGB

Setting the hard disk to the new size

You can change the size of the disk using the Set-HardDisk command. Here I’m going to assume the size of the disk needs to be increased by 10GB

$space_to_increase = 10
$new_capacity = $vm_info_dsk + $space_to_increase

Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq "Hard Disk 1"} | Set-HardDisk -capacityGB $new_capacity -confirm:$false

How it all comes together (the complete script)

Lets see how to put this all in to a robust expandable script.

The script will read a list of vms from the vm_list.txt text file (vms should be separated by a new line) which would be useful for bulk list of VMs – for more clarity on Get-Content read that post here. To change any parameter (different disk size, different harddisk) simply change the relevant variable.

#Read list from a text file vm_list.txt - separated by a new line
$vm_list = Get-Content "vm_list.txt"

$vcenter = "vCenter1"
$space_to_increase = 20
$hard_disk_number = "Hard Disk 1"

Connect-VIServer $vcenter 


foreach($vm_name in $vm_list){
	write-host "Fetching information of $vm_name"
	$vm = Get-VM $vm_name

	$vm_info_dsk = (Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq $hard_disk_number}).CapacityGB

	Write-Host "$vm $hard_disk_number is $vm_info_dsk"

	$new_capacity = $vm_info_dsk + $space_to_increase

	Write-Host "new hard disk capacity $new_capacity"

	Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq $hard_disk_number} | Set-HardDisk -capacityGB $new_capacity -confirm:$false

#Check if it is successfully set and display the new size
	$vm_info_dsk = (Get-HardDisk -VM $vm_name | Where-Object {$_.Name -eq $hard_disk_number}).CapacityGB

	Write-Host "$vm new hard disk 1 is $vm_info_dsk"
}
	
Disconnect-VIServer -Confirm:$false