Sorting bits into bytes...

VM Age in vROps

There is a very good article on the Cattle/Pet analogy. I won’t explain it in detail, as you can simply follow this link and get to know all about it. But in short it comes down to this. You take care of your pets, ea. you patch them and add features to them. Cattle on the other hand you don’t care for that much. You simply redeploy them on a regular base (which also is a form of patching).

Now that you know this, you can imagine that cattle VMs don’t have a very long lifespan. As they are not patched there should be a way to monitor that they are indeed redeployed every now and then. We have set the max age for cattle VMs on 90 days. They are deployed with a lifespan of 30 days. Which the end user can extend by 2x 30 days. After that the VMs are permanently deleted or are redeployed based on a new OS image.

The problem is that there is no way to monitor if a VM is older than 90 days. In order to get that done, here is a script that adds the age as a metric to the VM.

There are some caveats to note;

  • The scripts looks up the creation date. This is the date when that object was first discovered by vROps. So if you would have redeployed vROps yesterday, all your VMs will have an age of 1.
  • If you remove a cloud account with all its associated objects and reconfigure the same cloud account again, the VM age will be counted from that day forward.



Author: Kabir Ali -
Scriptname: VMAge
Version: 1.0 (Tested)
Date: December 23 2020
Why: According the whole cattle/pets philosophy cattle VMs have a max age of 90 days. After which they should be re-deployed. This script adds the VM age as a custom metric.
Remark: Test setup
        vROps Version 8.2.0 (16949153)

Example: .\VMAge.ps1 -vROpsServer "vROps.local.localdomain" -vROpsUser "Admin" -vROpsPass "VMware1!"



How it works:
Objects in vROps have a "creationTime" attribute. This is the timestamp when vROps first discovered the object.
Calculate the time between now and creationTime.
Add the outcome to the VM.


### Define Defaults ###

Param (

    [Parameter(Mandatory = $true)][string]$vROpsServer,
    [Parameter(Mandatory = $true)][string]$vROpsUser,
    [Parameter(Mandatory = $true)][string]$vROpsPass

# Convert timestamps
function ConvertTo-UnixTimestamp {
    $epoch = Get-Date -Year 1970 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0	
 	$input | % {		
        $milliSeconds = [math]::truncate($_.ToUniversalTime().Subtract($epoch).TotalMilliSeconds)
        Write-Output $milliSeconds
function Get-TimeStamp {
    return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)

# Bypass SSL certificate verification
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

### vROps API Connection ###

# Building vROps API string & invoking REST API
$vROpsURL = "https://" + $vROpsServer + "/suite-api/api/"
$vROpsAuthURL = "https://" + $vROpsServer + "/suite-api/api/auth/token/acquire"
$Type = "application/json"
# Creating JSON for Auth Body
$AuthJSON =
  ""username"": ""$vROpsUser"",
  ""password"": ""$vROpsPass""
# Authenticating with API
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Try {
    $vROpsSessionResponse = Invoke-RestMethod -Method POST -Uri $vROpsAuthURL -Body $AuthJSON -ContentType $Type
Catch {
    $error[0] | Format-List -Force
# Extracting the session ID from the response
$vROpsSessionHeader = @{"Authorization"="vRealizeOpsToken "+$vROpsSessionResponse.'auth-token'.token

# Clearing variable that is used to count the successful attempts
$modcount = 0
# Clearing variable that is used to count the unsuccessful attempts
$nomodcount = 0

# Get only virtualMachine objects from vROps
$ResourcesURL = $vROpsURL+"resources?resourceKind=virtualmachine"
# Build VMs list based on returned data
$vROpsAllVMs = (Invoke-RestMethod -Method GET -Uri $ResourcesURL -Headers $vROpsSessionHeader -ContentType $Type).resourceList

# Loop through the list of VM resources
foreach($Resource in $vROpsAllVMs) {
    # Convert creationTime to Human readable time as it is in Epoch time
    $EpochHumanTimestamp=(Get-Date 01.01.1970)+([System.TimeSpan]::frommilliseconds($Resource.creationTime)) 
    # Calculate days passed
    $DaysPassed = ((get-date) - $EpochHumanTimestamp).Days
    # In order to add/update the custom metric in vROps the current time is converted to Epoch time
    $EpochTimeNow = (Get-Date | ConvertTo-UnixTimestamp)
    # Create the JSON reply to update vROps
        "stat-content" : [ {
        "statKey" : "CustomMetrics|VM Age",
        "timestamps" : ['+$EpochTimeNow+'],
        "data" : ['+$DaysPassed+'],
        "others" : [ ],
        "otherAttributes" : { }
        } ]
    # Try to add/update the custom metric
    Try {
        $TagJSONResponse = Invoke-RestMethod -Method POST -Body $JSONData -Uri ($vROpsURL+'resources/'+$Resource.identifier+'/stats') -TimeoutSec 100 -Headers $vROpsSessionHeader -ContentType $Type
        $TagDetailedInfo = $TagJSONResponse.value
        $modcount = $modcount+1
    Catch {
        $error[0] | Format-List -Force
        $nomodcount = $nomodcount+1

# Status output
write-host "Update successful on $modcount VMs" -ForegroundColor Green
write-host "Update unsuccessful on $nomodcount VMs" -ForegroundColor Red

# Close session and done
Invoke-RestMethod -Method POST -Uri https://$vROpsServer/suite-api/api/auth/token/release -Headers $vROpsSessionHeader -ContentType $Type

Once that is done, it is pretty simple to create a dashboard to show the new metric. Here is an example of my dashboard:


I’ve created two environments based on tags, we use tags to mark the VMs as cattle/pets. Now for each type I get a nice overview of the age of the VMs.

Leave a Reply