Add Custom Properties to vRealize Operations Using the REST API and vRealize Orchestrator

Reading time: 7 minutes

I have been using vRealize Operations to monitor the compliance of virtual machines against the DISA VMware vSphere Virtual Machine STIG for quite some time now. With the release of the new VMware vSphere 6.5 Virtual Machine STIG, I have discovered that vRealize Operations does not collect all the necessary information out of the box to verify compliance with the new STIG rules. Rather than waiting for VMware to provide an update to vRealize Operations, I decided to utilize vRealize Orchestrator to add custom properties to the virtual machines in vRealize Operations using the vRealize Operations REST API.

If you would like to skip right to implementing the code, you can download the vRealize Orchestrator package using the link at the bottom of this post. Otherwise, let us dig into how we accomplish this.

Overview

Before we begin looking at the code, let us review at a high level the process implemented in vRealize Orchestrator.

Workflow Schema for Adding Properties to vRealize Operations Objects
  1. Add an HTTP REST host to vRealize Orchestrator
  2. Request an authentication token from vRealize Operations using the REST API
  3. Request a list of all managed objects from vRealize Operations with a resourceKind of VirtualMachine
  4. Loop through the list of VirtualMachine resources, find the Instance UUID, then utilize the UUID to find the matching VC:VirtualMachine object
  5. For each matching VC:VirtualMachine, obtain the configuration values and execute a REST API call to vRealize Operations adding the values as new properties to each VirtualMachine

Add a new HTTP REST Host to vRealize Orchestrator

Before making any REST API calls to vRealize Operations, we first need to create a new REST host using the built-in “Add a REST host” workflow. Use the following values when running the workflow:

Name: <vROPS FQDN>
URL: https://<vROPS FQDN>/suite-api/
Connection timeout (seconds): 30
Operation timeout (seconds) 60
Host authentication type: NONE

When the workflow completes, verify that the HTTP REST host was created by reviewing the vRealize Orchestrator inventory.

Requesting an Authentication Token from vRealize Operations

The preferred method for working with the vRealize Operations REST API is to utilize a bearer token for authentication purposes. We obtain the token by sending an HTTP POST request containing a username and password to:
https:///suite-api/api/auth/token/acquire

Per the REST API documentation (found at https:///suite-api/docs/rest/index.html), the following data is required in the body of the REST API request:

{
  "username" : "<username>",
  "authSource" : "<authentication source>",
  "password" : "<password>",
  "others" : \[ \],
  "otherAttributes" : { }
}

The “authSource” will likely be “LOCAL” if using a local user account, otherwise, it will match the name of your vIDM/SSO, or Active Directory authentication source. If successful, vRealize Operations replies with a token and its associated expiration time. An example response would look like:

 {
   "username" : "<username>",
   "authSource" : "<authentication source>",
   "password" : "<password>",
   "others" : \[ \],
   "otherAttributes" : { }
 } 

We accomplish this in vRealize Orchestrator with the below code:
Inputs: restAuthPasswod, restAuthUsername, restAuthDataSource, restHost

//Define the JSON request body
var jsonBody = {
    "username": "",
    "authSource": "",
    "password": "",
    "others": \[\],
    "otherAttributes": {}
};
//Fill in the values into the jsonBody
jsonBody.username = restAuthUsername;
jsonBody.password = restAuthPassword;
jsonBody.authSource = restAuthDataSource;
jsonRequestBody = JSON.stringify(jsonBody);
//Create a new HTTP REST Request object for the REST host that was provided
var request = restHost.createRequest("POST", "/suite-api/api/auth/token/acquire", jsonRequestBody);
request.contentType = "application/json";
request.setHeader("accept", "application/json");

//Attempt to execute the REST request
try {
    response = request.execute();
    jsonObject = JSON.parse(response.contentAsString);
    var authData = new Properties();
    //Return back the authentication token and validity period using a property set
    if (jsonObject.token != "" && jsonObject.validity != "") {
        authData.put("authToken", jsonObject.token);
        authData.put("authTokenValidity", jsonObject.validity);
        return authData;
    }
    else {
        throw "There was an errror executing the REST call.";
    }
}
catch (e) {
    throw "There was an error executing the REST call:" + e;
}

Request all VirtualMachine Managed Objects from vRealize Operations

Next up, we need to obtain a list of all of the managed VirtualMachine objects in the vRealize Operations inventory. To accomplish this, make a single GET request to the REST API using the following URL:
https:///suite-api/api/resources?resourceKind=VirtualMachine

We accomplish this in vRealize Orchestrator with the following code:
Inputs: restAuthData, restHost, resourceKind

//Request all objects from vROPs with a specified resourceKind
var request = restHost.createRequest("GET", "/suite-api/api/resources?resourceKind=" + resourceKind + ";pageSize=" + pageSize, null);
request.contentType = "application/json";
request.setHeader("accept", "application/json");
request.setHeader("Authorization", "vRealizeOpsToken " + restAuthData.get("authToken"));

try {
    var restResponse = request.execute();
    jsonResponse = JSON.parse(restResponse.contentAsString);
    return jsonResponse;
}
catch (e) {
    throw("Error executing the REST operation: " + e);
}

Find Matching VC:VirtualMachines by Instance UUID and vCenter UUID

For us to query properties from the VC:VirtualMachine objects in vRealize Orchestrator, we need to match the vRealize Operations VirtualMachines to vRealize Orchestrator VC:VirtualMachines. To accomplish this, we will use the VMEntityVCID which tells us exactly what vCenter server to use, and VMEntityInstanceUUID which provides us a unique identifier for the VM within the vCenter server. This allows us to determine which vCenter server to search and then provides the VM’s instance UUID which is guaranteed to be unique within a vCenter server. To find the matching VC:VirtualMachines, we use the VcPlugin’s SdkConnection method findByUuid. Then we query the values that we wish to store in vRealize Operations and send those properties to vRealize Operations using REST requests. In this case, we are querying two properties from the VC:VirtualMachine: “guestAutoLockEnabled” and “migrateEncryption.” The following code records the current time value so that all properties added during this execution of the code will have the same time value. It then generates an array of Properties that will be sent to the custom action “addProperties” which executes the REST calls to vRealize Operations.
Inputs: jsonResponse

var vropsResources = jsonResponse.resourceList;
var currentEpochTime = Date.now();
var updateCount = 0;

//For each vROPs VirtualMachine, find the matching vCenter VM
for each(resource in vropsResources) {
    try{
        var resourceIdentifiers = resource.resourceKey.resourceIdentifiers;
        var VMEntityInstanceUUID = null;
        var VMEntityVCID = null;
        var vm = null;

        //Get the VMEntityInstanceUUID for the vRealize Ops VirtualMachine object
        for each(resourceIdentifier in resourceIdentifiers) {
            if(resourceIdentifier.identifierType.name== "VMEntityInstanceUUID") {
                VMEntityInstanceUUID = resourceIdentifier.value;
            }
            if(resourceIdentifier.identifierType.name== "VMEntityVCID") {
                VMEntityVCID = resourceIdentifier.value;
            }
        }
        //Find the vCenter VM object by using the VMEntityInstanceUUID and VMEntityVCID returned by vROPs
        var sdkConnection = VcPlugin.findSdkConnectionForUUID(VMEntityVCID);
        if (!sdkConnection) {
            throw "No vCenter found with UUID " + VMEntityVCID;
        }
        try {
            vm = sdkConnection.searchIndex.findByUuid(null, VMEntityInstanceUUID, true, true);
        }
        catch(e) {
            System.log("Unabled to find VM with instance UUID " + VMEntityInstanceUUID);
        }
        //Found matching VM
        if(vm != null && VMEntityVCID != null && VMEntityInstanceUUID != null) {
            //Variable to hold the JSON Update Body
            var jsonBody = new Object();
            var jsonProperties = new Array();
            
            try {
                if(vm.config.guestAutoLockEnabled != null)
                    jsonProperties.push(generateProp("config|security|guestAutoLockEnabled",currentEpochTime,vm.config.guestAutoLockEnabled));
            } catch(e) {}

            try {
                if(vm.config.migrateEncryption != null)
                    jsonProperties.push(generateProp("config|security|migrateEncryption",currentEpochTime,vm.config.migrateEncryption));
            } catch(e) {}

            //Send properties to vRealize Ops
            var responseCode = System.getModule("com.stevenbright.vrops.resources").addProperties(restHost, restAuthData.get("authToken"), resource.identifier, jsonProperties);
            System.log("Submitted request to add " + jsonProperties.length + " properties to VM '" + vm.name + "'. Received the following HTTP response code: " +responseCode);
            updateCount = updateCount + 1;
        }
        else {
            System.log("vROPs VM " + resource.name + " not found matched with vCenter VM.");
        }
    }
    catch(e) {}
}
System.log("Updated " + updateCount + " VMs with new properties.");

function generateProp(statKey, timestamps, values) {
    if(statKey!=null && timestamps!=null && values!=null) {
        var newProp = new Properties;
        newProp.put("statKey", statKey);
        newProp.put("timestamps", timestamps);
        newProp.put("values", values);
        return newProp;
    } else {
        return null;
    }
}

The action "addProperties" that we use to send the properties to vRealize Operations using REST calls is:

//Variable to hold the JSON Update Body
var jsonBody = {
        "property-content" : \[ \]
        }

//Create and add stat-content entries
for each(newProperty in newProperties) {
    var jsonProperty = {
            "statKey" : "",
            "timestamps" : \[ \],
            "values" : \[ \],
            "others" : \[ \],
            "otherAttributes" : { }
        }
    try{
        jsonProperty.statKey = newProperty.get("statKey");
        jsonProperty.timestamps.push(newProperty.get("timestamps"));
        jsonProperty.values.push(newProperty.get("values"));
        jsonBody\["property-content"\].push(jsonProperty);        
    } catch(e){}

}

//Prepare to execute the REST request
var request = restHost.createRequest("POST", "/suite-api/api/resources/" + resourceId + "/properties", JSON.stringify(jsonBody));
request.contentType = "application/json";
request.setHeader("accept", "application/json");
request.setHeader("Authorization", "vRealizeOpsToken " + restAuthToken);

//Execute the HTTP REST request
try {
    response = request.execute();
    return response.statusCode;
}
catch (e) {
    System.error("Error executing the REST operation: " + e);
}

When executed together, the code will add two new properties to our virtual machines in vRealize Operations. The new properties can then be used to create new Alert Symptoms to address the four missing VMware vSphere 6.5 Virtual Machine STIG checks as shown below.

vRealize Operations Symptom Definition dialog for additional VMware vSphere 6.5 Virtual Machine STIG checks

See Also


Search

Get Notified of Future Posts

Follow Me

LinkedIn Icon
Twitter/X Icon
Threads Icon
RSS Icon

Recent Posts