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.

Checking for empty returns from MySQL queries in Powershell

Recently during a project I had to some DB queries using powershell. While that is straightforward and easy enough (I will create a post on querying DBs with powershell later on), the problem I ran in to was checking for an empty return.

I wouldn’t blame you for thinking “but scriptigator this should be simple right?” – because I thought the same. Query the DB, check if its null, presto manifesto yes? Well… sort of – IF you’re using Invoke-MysqlQuery this will work

$query_result = Invoke-Mysql -Query "SELECT * FROM table_name"

if($query_result -eq $null){
    Write-Host "This will work if you're using invoke-mysql"
}

else{
    Write-Host "need to try something else"
}

The problem with this approach is that empty or the $null global variable in Powershell is not exactly the same as an SQL null value. So we will have to use a workaround to get around the issue. There are a couple of ways to do this :

1. Checking the row count of a query

If the query does not match any records, how many rows would it output? Should be 0 right? Let’s use that logic here

$query_result = Invoke-MysqlQuery "SELECT * FROM table_name"
$query_row_count = ($query_result).count
if($query_row_count -eq 0){
    Write-Host "This WILL work!"
}

And yes this will work!

2. Using DBNull

If you’re using SQL reader method to query the Database you can use the system provided value of DBNull or System.DBNull that makes life a little easier. ( I had trouble with this when using Invoke-MySQL – script life can’t get too easy now can it? )

//DBNull and System.DBNull are interchangeable

if($query_result -eq [DBNull]::Value){
    Write-Host "This will work if you're using sqlreader"
}


if(([DBNull]::Value).Equals($query_result)){
    Write-Host "This will work if you're using sqlreader"
}

Conclusion time…

So there it is – all the methods I found to check for empty returns in Powershell/PowerCLI. As you know I do not claim to be an all-knowing demigod in scripting so if you find any alternative methods to these please feel free to share!

Till the next time – happy scripting!

Reading Text files with Powershell | Get-Content cmdlet

More often than not when scripting you will have to deal with reading files with Powershell when scripting. Whether its logs or files comparison orrrr feeding a list to the script to store and use as a variable, you will need to read a file from your script. Thankfully Powershell has the Get-Content cmdlet which will read a file with just one line of code.

Using Get-Content Cmdlet to read a text file

Get-Content "D:\text_file.txt"

And that’s it! that’s all you need to do! And it will read the file like this

Let’s see how we can play around with this shall we!

Retrieving specific lines

Powershell Get-Content Cmdlet takes in each line as an array. So the 1st line will be in the 0th element, the 2nd line will be in the 1st element and so on… To play around with this concept we need to get the content to a variable

$text = Get-Content "D:\text_file.txt"

So $text[0] which is the 0th element will contain line 1, $text[1] which is the 1st element will contain line 2 and so on…

Traversing a list and Searching for text

To explore this concept let’s get a more practical example. Let’s say our text file is a list of server names that we manage as follows

Let’s say we need to filter out just the windows servers from the list. This can be done as follows by searching for the “windows” phrase.

Get-Content "D:\server_list.txt" | Where-Object {$_ -like '*windows*'}

Now lets say we need to connect to all of the windows servers (hypothetically). We can traverse this list with a for-loop (I am not going to use a Enter-PSSession, but for clarity of this exercise I’m just going to display a text – you can do the hard work on your own 😉 )

$windows_servers = Get-Content "D:\server_list.txt" | Where-Object {$_ -like '*windows*'}

ForEach ($windows_server in $windows_servers){

	Write-Host "Connecting to $windows_server"
	#Alternatively here is where you would use Enter-PSSession -ComputerName $windows_server

}

So that’s about it. I’ve just touched the surface of what you can do with Get-Content. As time permits I will do a more elaborate post on search and loops. Till then – keep scripting!

Storing Passwords securely with Powershell

Security is paramount in our line of work, a breach would mean a catastrophe of epic proportions for a sys admin – I mean what can’t you do if you’re the administrator right? So we set our passwords T0b3imp055ibleT0Guess and lock them up in keypasses.

But we sys admins are lazy, when it comes to scripts – we leave it to ask for our password every time we execute the script, but what if we need to automate the script – schedule it to run automatically? we can’t leave them in plain sight, plain text! Not to worry – as always powershell has a module to save our skins. Here’s how you store your passwords securely and retrieve them when needed:

Storing passwords securely

Powershell thankfully has the ConvertFrom-SecureString module to convert any text to a secure string. Save the following script separately and it will prompt you to enter a new password and will save it in the location with the file name you have specified

$password_file_location = "D:\test\credentials.txt"
\\location and filename can be anything you want it to be

Read-Host "Enter New Password" -AsSecureString |  ConvertFrom-SecureString | Out-File $password_file_location

Retrieving the saved password

Use the following lines in your script (that does the automation job) to retrieve the saved password

$password_file_location = "D:\test\credentials.txt"

$password = Get-Content $password_file_location | ConvertTo-SecureString

Now the $password variable contains the password you have saved previously. Use it in the part of the code you use to connect to the server and viola you’re through without having to type your password all the time or using a plain text password!

Note: This doesn’t guarantee security, but it’s definitely a step in the right direction!

Error handling in Powershell : Try / Catch blocks in Powershell

Recently I had a project to develop a web portal that would trigger a powerCLI script. The problem was that if anything except the required JSON output popped up, especially any errors, the whole thing would crash. The only work around was to error handle the sh#t out of the code!!!

The best and easiest way to error handle / catch exceptions is to use try / catch blocks – if you’re familiar with Java (or any kind of) programming, powershell try catch blocks work exactly the same way. If you’re not – don’t worry I got your back.

What is a try catch block?

Try catch block is a way to structure your code to handle errors and catch any exceptions that your code might throw. There is also a “Finally” block that is also a part of the whole thing.

Try block – This is where you place the code which you need to check for exceptions
Catch block – This is where you specify what you need the code to do IF an exception is caught
Finally block – This is where you specify the code that you need to run regardless of whether an exception was caught or not

try{

	//The code that needs checking for exceptions
}

catch{

	//What should happen if an exception is caught
}

Finally{

	//What should run regardless of what happens above
}

Code Example

try{

	Connect-VIServer $vcenter -Credential $credentials -ErrorAction Stop
	Write-host "This will not run if an exception is caught"
}

catch{

	Write-host "Oops something is wrong with the credentials"
}

Finally{

	Write-host "This runs regardless of what happens above"
}

The above code is a PowerCLI script which will try to connect to a vCenter with the given credentials and if an exception is caught it will display the text in the catch block.

IMPORTANT: DONT FORGET THE -ERRORACTION The -ErrorAction stop is extremely important for the code to function as expected. This is will stop powershell from executing the next lines and divert the code to the catch block. If ErrorAction was not there it will continue executing everything regardless of an exception.