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!

Why you as a Sys Admin might be hesitant to script!

Scripting is addictive, it is the tool of the lazy and the weapon of the efficient. While some like me are hooked onto scripting, most do not share this passion. In my few years in the industry, I have noticed that while most Systems Engineer’s do leverage scripts, they do not truly take to it. Some would be hesitant to develop a script, some are happy picking up some one liner from the web and being done, while some would opt not to script at all.

In this post I thought to list out some of the reasons I have seen why people don’t script and dispel some of the myths :

1) But මචං (pal) I am not a programmer

This is the most common answer I get when I ask “why don’t you make a script for it?”. While I do come from a background of Software Engineering, in my opinion System’s Engineers do the hardest part of coding – debugging someone else’s code!

If you think about it systems and datacenters today are all software based. Troubleshooting system/infrastructure errors is actually troubleshooting issues in code that someone else has made – which is the toughest thing possible!

If you are able to troubleshoot logically the issues of a code, that means you can definitely write a small piece of code that simplifies your work! It’s just a matter of mindset.

2) Not using coding elements that make life a lot easier

Scripting can get cumbersome and not save as much time as advertised if you’re not using coding elements. I’ll admit, here is where some coding background would come useful, but they are not difficult to pick up.

Here’s a little example, say you have to power down a bunch of virtual machines in your vCenter, you can do it like this:

Get-VM VM1 | Shutdown-VMGuest
Get-VM VM2 | Shutdown-VMGuest
Get-VM VM3 | Shutdown-VMGuest

This does get the job done, but its terribly inefficient. Say this list is 100s of VMs, you would be writing 100s of lines to get this done. Would be easier to go to the vCenter and do it manually at that point! And if your application team says “Oops, sorry guys that was the wrong list of virtual machines” well all that effort is down the drain!
Here’s another, more simple way of doing the same thing :

$vmList = "VM1", "VM2", "VM3"

ForEach ($vm in $vmList){
    Get-VM $vm | Shutdown-VMGuest
}

This is a much more efficient piece of script. If you need to edit the list of VMs all you have to do is edit that one array variable. Your script is only a few lines long and the application team can edit this as much as they want and you don’t have to sweat about it. Also now you have a ready made script that you can use whenever another request like this comes along!

3) Not making scripts for the long term

Okay, this point also directly relates to the last point. One thing we’ve all done when we get a problem we need a script for is find a script/one-liner, execute and be done. But for scripts to be truly time-saving and effective they need to be applicable in the long term. For that you have to be mindful of concepts like code extend-ability and efficiency.

Now they might sound like boring, complex terms but the concepts are really simple. When making a script, make sure that if you have to re-use it the parts you have to change are minimal and what you have to change is clear. This is where the use of variables with names that make sense and comments come in to play.
Let’s take an example about increasing the Memory of a virtual machine:

Connect-VIServer vCenter1
Get-VM VM1 | Set-VM -MemoryGB 8 

This is perfectly fine and gets the job done… for this Virtual Machine.
Now take a look at this script:

#Set the parameters
$vcenter_name = "vCenter1"
$vm_name = "VM1" #for multiple VMs go as "VM1", "VM2" ...
$memory_amount = 8 

Connect-VIServer $vcenter

ForEach($vm in $vm_name){

    Get-VM $vm | Set-VM -MemoryGB $memory_amount 
}

With few lines of code added we have created a clear, extensible script that you can share with your colleagues and be a hero. We went from a script focused on a particular virtual machine to one that we can easily choose a different vCenter, have multiple VMs, and change the memory amounts easily. By using comments we have ensured that even if you dig this script up a year later, you can easily pick up what to do!

4) Uhh… I had a script for this, but I can’t find it now

Remember when you got that one problem, and you searched the web for a script/one-liner, got it done then forgot about it? Well now it has come up again and now you have to do the same thing again? Or your friend is asking for the script and now you’re the dumdum who can’t find it? Yes, we’ve all been there.

Programmers use a code repository or repo to keep the code, and you as a Sys Admin can do the same thing. It doesn’t have to be as fancy as a GitHub repo (even though it makes life easier with version controls and all), it could very well be a Google drive or even just a well organized folder in your PC! Just ensure that your scripts are stored somewhere which is easily accessible and searchable in the long term.

Your future-self will thank you for it!

In Summation

Just follow these simple tips to make your life easier with scripting:

  • Believe that you are able to script – don’t just dispel it thinking its something that only “some people” can do. All you need is a little practice.
  • Learn a few coding tips and tricks to make a more versatile script.
  • When you’re making a script keep in mind to make a script that you can dig up years later and use it easily. Think of your future-self!
  • Most importantly make sure you have saved your script somewhere that you can easily find!

And that is it! Hope you’ve enjoyed this post and feel like making more scripts that you’ll be proud of!

If you liked/hated this post, want me to share some detailed tips and tricks or elaborate more, please do hit me up in the comments, or on my social media linked below!

Thanks for reading and 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 😉

System.Data.DataRow issue when querying MySQL with PowerShell Solved!

Has this happened to you – you’re doing a SELECT query from powershell doing your thang, but when you’re trying to output the fruits of your query labour all it says is System.Data.DataRow? You’re sure you did the query right, while the issue seems to be common the internet is not helping you out either and you feel like the scripting world is against you! Don’t worry the Scriptigator got yo’ back brotha/sista!

Okay in all seriousness, the issue does seem fairly widespread and weirdly did not happen to me before except this one time. Output is just “System.Data.DataRow” without outputting the contents of the query. The issue is that PowerShell seems to encapsulate the entire row in an object.

<query_result>.Item to the rescue!!! *this is only needed if the query returns just 1 row.

<query_result>.Item("<item_name>")

The item name is the column name that you want to output. As an example the code would look like this

#printing out the query results
Write-Host $query_result.Item("column_name")

And there it is, a fairly simple answer to a frustrating issue.