Enhance your VDI 3xperience with ZeeTransformer

Today’s blog post is all about ZeeTransformer – It’s a product by a company called ZeeTim which allows conversion of any PC using their Linux-based ZeeOS into a Thin or Zero Client.

It all started when I was looking for a VDI solution for one of my friend who wanted to setup a lab for his new venture. We needed an endpoint solution which would prevent us from having to duplicate effort in maintaining Windows desktops in the datacenter as well as on the endpoint. We needed something secure, low maintenance, and that would give good performance for the users. Then, I came across this tool ZeeTransformer which was just a bootable key away from our pocket-friendly VDI journey.

I was so impressed with the ease of setup, configurability and PC repurposing capabilities of ZeeTransformer that I thought I should write about it.

For the ProTip, you can point to this link https://www.zeetim.com/zeetransformer-free-trial/

Overall Experience

Before I go into how I did the installation, I would like to share the features I loved the most about ZeeTransformer:

  • Independent of underlying hardware: Any PC that would support Linux (let me tell you, most of them does 😉) can become a thin client and can be a part of your VDI solution. Saved a lot of money 💰 as we repurposed our old systems.
  • Management is a breeze 💨: As ZeeConf can centrally manage various aspects like patching, 3rd party app installations, security, display settings and lot more, managing the solution and applying new settings is just few clicks away.


With just a little reading on www.zeetim.com, I was good to setup the whole process. We were using a set of 10 old PCs for our lab. For managing them, I used ZeeConf client which comes along with ZeeTransformer out of the box. Let’s me show it in a quick and dirty way.

  • Download ZeeTransformer from here on your PC.
  • Extract the downloaded zip and it should contain 2 folders and testing procedure files:
    • ZeeConf
    • ZeeTransformer
  • Connect a USB key to the PC which will be converted to a bootable key.
  • Go to ZeeTransformer folder in the extracted zip.
  • Launch “ZeeTransformerImagerLite.exe”.
  • Select the inserted USB key as a device.
  • Check “Eject after creation” checkbox if it is not checked by default. This will automatically eject the key after creation.
  • Click on Create boot device to transform the USB key.
  • Eject the key after completion.

Once the USB key is created:

  • Connect the USB key to a PC that you want to use as a thin client.
  • Restart the PC and go to the BIOS settings
  • Disable “Secure Boot” option, select the inserted USB key as a boot device, save the settings and restart the PC.
  • Once the PC boots via the USB key, the device is ready to be used.

ZeeTransformer UI

PC boots up in just a fraction of seconds. The Home screen has a minimalistic interface with few applications pre-installed like Google Chrome, Mozilla Firefox, Citrix Client, Horizon Client etc. for easy access to existing infrastructure for bigger organizations.

Now that we have thin client nodes ready to be consumed by end-user, the next step is to manage them.

Client Management using ZeeConf

As a part of complete VDI solution by ZeeTim, ZeeTransformer comes with a central management tool called ZeeConf.

So, After I converted our PCs to ZeeTerms (means a PC with ZeeTransformer), I had to install the ZeeConf management tool to my main PC. Let’s see how I did that. 

  • On all the newly created ZeeTerm, open ZeeConf Lite (icon on the desktop) and check the IP address on the home page.
  • On main PC, go to the ZeeConf folder from the folder we extracted earlier.
  • Launch ZeeConf.exe

If the PC and the newly launched ZeeTerm are in the same network, ZeeConf will automatically detect it. If they are on the different networks:

  • Click on “Search for new terminals”
  • Enter the IP address of your newly created ZeeTerm and click OK.

Now, once your node is connected, you can manage all sort of settings like Infrastructure, Display, Packages, Security, Network, Proxy, VPN etc. as well as configuration for VMware, Nutanix, Citrix, etc.

For further reference please refer their official guide here ZeeConf Client – User Guide.

Licensing using ZeeLicense

If you followed the above process, you might be able to test the ZeeTim VDI solution because client will continue to work for 1 hour. However, use it further more, you would need licenses.

ProTip I used the 10 free license that ZeeTim provides during registration for our lab setup.

  • Download ZeeLicense Server from the Downloads section.
  • Install it using instructions shown here.
  • Launch the installed ZeeLicense Administrator as an administrator
  • In ZeeLicense Administrator, click License request to request a new license
  • It will open the below window:
  • Fill the details and Click OK.
  • A license request file will be generated in the installation directory of the ZeeLicense Server. (Default: C:\Program Files\ZeeTim\ZeeLicense Server)
  • Send this license request file via email to support@zeetim.com and ZeeTim will send back to you a .zip file containing the requested license.


If you are already impressed, I would inform you that there is more to it. Using ZeeTransformer in a enterprise will require other set of tools as well like Centralized Printing solutions, User Access Management, Virtualized Infrastructure Support etc. For that, they have a set of enterprise-grade tools like ZeePrint, ZeeOTP, ZeeEdge etc.

Know more about them at www.zeetim.com

Final Verdict

As the title suggests, the experience was 3x compared to other VDI endpoint options. OK! maybe its not 3x but it is much better than other VDI endpoint solutions out there at least in our case as it allowed old PC repurposing and costed us almost nothing with all the great features 👍. Also, the support the ZeeTim team provided was really great. The team reached out to me to see if everything is fine and we had some great discussions and sessions to help me even better understand the process.

Subscription received!

Please check your email to confirm your newsletter subscription.

(Un)Official tools & extensions for vRealize Orchestrator

Disclaimer This list contains tools which may not be fully tested for production use. Use them at your own risk.

Over the years, working with vRealize Orchestrator, I have noticed that the vRO community is quite a strong community with a lot of competent folks. I saw a lot of work going around to revamp the way vRO works and make it an even funnier tool to work. Even though vRO is a proprietary software, a lot of Open-source work has been done circling it since it was invented. This post tries to list out most if not all of them and focuses on providing a bigger picture to all the readers that on how they can develop around vRO as well, instead of just developing inside it. Keep in mind that I am not listing open-source plugins, scripts or packages. Also, If you know any other tool that can be added to this list, I would thank you and add it here. Here is the list.

Let’s learn more about them in details below.


This repository is developed by Lior Abitbol. It is a simple Python library to interface with VMware vRealize Orchestrator. What you can do with vmwvro:

  • get workflow information
  • start a workflow
  • monitor a workflow run

Know more: https://github.com/LiorAbitbol/vmwvro


vRODoc is a PowerShell script, developed by Mayank Goyal, that connects with your vRealize Orchestrator to fetch a package that contains all your action modules and action items, intelligently add JSDoc annotation to those action items and convert them into html pages which can be presented as a web-based code documentation. See a live example here. You can also add other JSDoc comments to your actions.

Know more: https://github.com/imtrinity94/vRODoc

vrotsc (vRO TypeScript Compiler)

vrotsc, developed by internal team at VMware, is a custom TypeScript to JavaScript compiler using the TypeScript Compiler API. It transpiles TypeScript code into a vRO-compatible JavaScript by handling module resolution, classes, polymorphism and ECMAScript 6 shims. The object-oriented paradigm is achieved by using an external class library or by generating a small piece of code at the top of each file, which will wire the object prototypes (just like the original TypeScript compiler). Support for ES6 means you can use new array APIs and data structures such as Map and Set as if they are built into the vRO engine.

The compiler is written in NodeJS and can be distributed as a single executable for Windows, MacOS, and Linux.

Know more: https://blogs.vmware.com/management/2018/11/vro-development-with-typescript.html



The vRO Assistant (vROAST) was a free community tool developed initially for internal usage by ITQ. It accelerated vRO code development and creates strong source control (GIT) integration. vROAST was distributed in 2 flavors: standard OVA and a docker image. However, this project was officially terminated.

Know more: https://vroast.io/

vRO XML Explorer

vRO XML Explorer is a Visual Studio Code plugin created by Chris Alleaume that edit existing vRO Workflow Scriptable Tasks and Action code directly by hooking straight into your linked git repo. It enables editing of Workflow and Action code straight from VSCode, without having to use Buildtools, Maven and the relatively complex process associated. Hence, gives various features like advanced linting & more robust code. It support Polyglot vRO runtimes viz. JavaScript, NodeJS, PowerShell & Python.

Know more: https://marketplace.visualstudio.com/items?itemName=ChrisAlleaume.vro-xml-explorer


The vRealize Orchestrator API Explorer is a popular reference site for people working with vRealize Orchestrator (vRO). You can use it to all the JS types supported by vRO along with their methods and attributers. It is developed and maintained by Ruurd Keizer.

Know more: https://www.vroapi.com/


[supports upto vRO 7.x]

vRealize Orchestrator Command Line Interface, developed by internal team of VMware, is an utility which can be used to simplify the coding with vRealize Orchestrator. You can make different actions from a vRealize Orchestrator (vRO) server :

  • pull : export a vRO package of a workflow to a local repository
  • expand : expand a local vRO package to a local files structure and permit to edit easily your code It also permit to upload to a vRO server :
  • build : Build a package file from the local files structure, it will take your code and rebuilt it as a vRO package.
  • push : Push/upload a package (and optionally build it) to a vRO server.

Know more: https://communities.vmware.com/t5/vRealize-Orchestrator-Documents/Technical-Preview-of-the-Command-Line-Interface-for-VMware/ta-p/2794955

vroparse/vRealize Orchestrator Parser

vRealize Orchestrator Parser is a fling tool developed by Jim Sadlek and allows user to extend the vRealize Build Tools Fling toolchain or to be used as a stand-alone with the Export Package to Folder option in native vRealize Orchestrator(vRO).

vRealize Orchestrator Parser parses vRO workflow XML files and extracts programming language code (Javascript, Python, Powershell, etc) and stores it as discrete files, that can then be checked into a source code control system, and or edited directly as discrete programming language source code from a traditional text-based source code editor, such as Visual Studio Code. These discrete files can also be consumed by other, third-party CI/CD systems like Maven and Jenkins. They can be edited, and they can be imported back into vRO workflow XML files. ‘Diffs’ and changes on the resulting code files are easily observed and tied to SCCS version numbers and releases, and can easily be merged and branched through normal software engineering development practices.

Know more: https://pypi.org/project/vroParse/ & https://flings.vmware.com/vrealize-orchestrator-parser



vFLOWer Toolkit was developed by ByteLife Solutions, enabled exchanging VMware vCenter Orchestrator content (workflows, actions, etc) as a versionable source code in XML format. Along with its pre-requisite components vFLOWer allows vCO administrators and/or developers to perform the following actions:

  • Retrieve vCO content source code in XML format from remote version control repositories and build a binary vCO package to be imported into vCO.
  • Publish vCO content source code in XML format to remote version control repositories by unpacking/converting exported binary vCO packages.

Know more: https://github.com/Everything-VMware/vFLOWer


[supports upto vRO 7.x]

This repository is developed by Daniel Linsley. It allows user to access to the vco-repo Maven repository for vRealize Orchestrator plugin and package / building and testing without deploying a full-fledged vRO VM, this Dockerfile builds a nginx based container that will host the contents of the /vco-repo repository.

Know more: https://github.com/Everything-VMware/docker-o11n-repo


learnorchestrator.github.io is a website developed by Sam McGeown and Moff designed to be a living technical reference focused on providing in-depth information on vRealize Orchestrator. This website is visually great and engaging and supports vRO 8.x.

Know more: https://learnorchestrator.github.io/


[supports upto vRO 7.0]

PowervRO is a PowerShell module developed by Jakku Labs built on top of the services exposed by the vRealize Orchestrator REST API.

Know more: https://github.com/jakkulabs/PowervRO


This repository was developed by Ludovic Rivallain. vRO-package-diff is a Python package to compare 2 VMware vRealize Orchestrator packages.

Know more: https://github.com/lrivallain/vro-package-diff


This PowerShell Module by Garry Hughes housing a set of functions designed to assist with the creation and update of VMware VRO Actions.

Know more: https://github.com/imtrinity94/vROIDE


This gist again by Daniel Linsley allows user to create a certificate for signing vRO packages which can be easily injected in already existing vRO packages by simply replacing the old .cert file with the new one.

Know more: https://gist.github.com/imtrinity94/2a8b71f4513f6dfde2b19a16d12c7ab2


This repository is developed by Tony Skidmore. It contains Ansible module for executing vRealize Orchestrator workflows.

Know more: https://github.com/imtrinity94/Ansible4vRO


This module existed somewhere around 2009-10 which was nothing but a Python module to interact with vRO. However, no more information available about this module on Google right now.

That’s all in this post. I hope you like it. Feel free to share it with your vRO colleagues. Created with ❤️.

Tip around System.log() for Big Workflows with Scriptable tasks and actions

  1. Introduction
  2. Concern
  3. Possible Solution
    1. Workflows
    2. Actions


It’s quite evident that vRO Workflows can grow huge for major provisioning tasks like hardware deployments, vApp deployments or tenant provisioning etc. just to name a few. And in a big environment, such workflows can come in large numbers and may be coupled with various other workflows quite generally for eg. using Nested WFs.


Now, the real pain starts when you have to debug them. for Debugging, developers relies mostly on System.log() or System.debug() to know about the statuses and variable values etc. up to a particular point, which is really great.

If I talk about myself, I even uses a start and completion log for every Scriptable task in a workflow. This always pinpoints to the scriptable task that was started but couldn’t complete due to any reason and therefore couldn’t print the end log. Let me explain it. There is a workflow (Create a VM) and there is a scriptable task (Get All VM Names). In this scriptable task, I would add some thing like

System.log("\"Get all VM Names\" script started");

and at the end of this script

System.log("\"Get all VM Names\" script completed");

Imagine this in a very large workflow, this can really help. But I knew this needs to be improved. Sometimes, you changes the content of a scriptable task and its definition changes, so update it’s name -> Get All VM Names to Get All VM Names and IPs. In such cases, you have to update those start and end log statements. And I hate that!

Possible Solution

I referred to the vRO community (link) and found one interesting way out.


We can use this,

System.log("\""+System.currentWorkflowItem().getDisplayName()+"\"  script started");
System.log("\""+System.currentWorkflowItem().getDisplayName()+"\"  script completed");

This will automatically print the updated name of the scriptable task.

vRO Workflow Screenshot with scriptable tasks | Image by Author

You can also print the item# if you want, using this command



Now, this is a little tricky. We can’t do the same for actions. However, if we consider them as a part of a workflow, which means they will act similarly as an item and obviously will have a item number, then we can print their start and end cycle. We can do so by adding the below mentioned script in your actions.

System.log("\"" + System.currentWorkflowItem().getDisplayName() + "\" action started");
System.log("\"" + System.currentWorkflowItem().getDisplayName() + "\" action ended");
A vRO Action | Image by Author
vRO Workflow Screenshot with scriptable tasks and action | Image by Author

Here, we added a condition just to avoid printing Ended "" script if you try to run your actions solely.

A vRO Action | Image by Author

Generate vRO Workflow Runs Report in CSV

TL;DR Download the package from here & edit the inputs and run it to get a CSV Report for last 30 days of Workflow Runs in vRealize Orchestrator.

Many a times, we have environments where we want to monitor the executions of vRealize Orchestrator Workflows, like how many times a workflow is run, who executed the workflow, or what was the success rate of a particular workflow, or maybe just want to check how long it take to execute a workflow. I have seen few customers that were looking for similar solutions. So, If you are also looking for a way to achieve something similar, this article could be of some help.

As we know, vRO offers REST API out-of-the-box for all kind of operations. Same is true for getting the facts and figures of its workflow execution. There are various ways to get that kind of information but I find using the catalog-service-controller module to be the quickest for my use-case. Let’s see it in detail below.

Understand the REST API

The catalog-service-controller module is quite a versatile API module in vRO. You can use it to dig-in information for a extensive set of entities. However, the path and method we are mostly interested in today is, GET /api/catalog/{namespace}/{type}.

Namespace = System
type = WorkflowToken

where date30DaysAgo will get its value from getDate30DaysAgo() action and currentDate from getCurrentDateInISOFormat() action available in the vro-package.

Note You can add more states to this query like waiting, canceled, etc. if you are interested in workflow tokens other than completed and failed.

Understand the Code

In the package attached, we have a workflow Workflow Runs Report Generation which will have the code divided in 3 parts. For ease of operation, the workflow is creating a transient REST host using user provided inputs but you can also add your vRO as a REST Host in the inventory and add this REST URL as a REST Operation with dates as variable input. Also, in Part 2/3, only 5 properties are added to the CSV data. You can lookup for additional properties related to a workflow token if you want a more details-heavy report.

//Part 1/3: Invoke Rest Operation

//Part 2/3: Gather data in CSV format
var reportCSV = "Name,Status,Start Date,End Date,Started by\r\n";
if (response.statusCode != 200) { // 200 - HTTP status OK
    System.warn("Request failed with status code " + response.statusCode);
} else {
    var doc = JSON.parse(response.contentAsString);
    System.log("Number of failed or cancelled tokens: " + doc.total);
    for each(var link in doc.link) {
        for each(var attribute in link.attributes) {
            if (attribute.name == "name")
                var wfName = attribute.value;
            if (attribute.name == "state")
                var wfState = attribute.value;
            if (attribute.name == "startDate")
                var wfStartDate = attribute.value;
            if (attribute.name == "endDate")
                var wfEndDate = attribute.value;
            if (attribute.name == "startedBy")
                var wfStartedBy = attribute.value;
        reportCSV += (wfName + "," + wfState + "," + wfStartDate + "," + wfEndDate + "," + wfStartedBy+"\r\n");

//Part 3/3: Send CSV data as a mail attachment

In Part 3/3, we are simply attaching the CSV data as a MIME attachment and send it to the toAddress.

Let’s Run it

  • Import the package available here. If you need help with package import, follow this guide here.
  • Go to workflow Workflow Runs Report Generation and Click Edit
  • Go to the scriptable task and edit these input values.
  • After editing the values, Save it.
  • Click Run.
  • If the workflow executed successfully, you will see in the logs, the mail is being sent to the provided email address.
  • Check your mailbox. If you can’t see the email in your inbox, probably check the email address that you’ve provided or check your SPAM folder.
  • Download the attached CSV file and Open it. You will see a sheet which will be identical to what you have in your vRO Workflow Runs Tab (Performance view OFF). You may also want to sort this sheet on Start Date column in descending manner.

Download vRO Package

That’s it in today’s post. I hope you will like it.

Subscription received!

Please check your email to confirm your newsletter subscription.

JavaScript to Java Conversion Limitation in vRO

When vRealize Orchestrator runs scripts, the vCenter Server plug-in converts JavaScript arrays to Java arrays of a fixed size. As a result, you cannot add new values to vCenter Server data objects that take arrays as property values. You can create an object that takes an array as a property if you instantiate that object by passing it a pre-filled array. However, after you instantiate the object, you cannot add values to the array.

For example, the following code does not work:

var spec = new VcVirtualMachineConfigSpec();
spec.deviceChange = [];
spec.deviceChange[0] = new VcVirtualDeviceConfigSpec();
System.log(spec.deviceChange[0]); //Output undefined

In the above code, vRealize Orchestrator converts the empty spec.deviceChange JavaScript array into the fixed-size Java array VirtualDeviceConfigSpec[] before it calls setDeviceChange(). When calling spec.deviceChange[0] = new VcVirtualDeviceConfigSpec(), vRealize Orchestrator calls getDeviceChange() and the array remains a fixed, empty Java array.

Workaround: Declare the array as a local variable:

var spec = new VcVirtualMachineConfigSpec();
var deviceSpec = [];
deviceSpec[0] = new VcVirtualDeviceConfigSpec();
spec.deviceChange = deviceSpec;
/* Output
DynamicWrapper (Instance) : [VcVirtualDeviceConfigSpec]-[class com.vmware.o11n.plugin.vsphere_gen.VirtualDeviceSpec_Wrapper] -- VALUE : (vim.vm.device.VirtualDeviceSpec) {
   dynamicType = null,
   dynamicProperty = null,
   operation = null,
   fileOperation = null,
   device = null,
   profile = null,
   backing = null

Disclaimer This article is drawn from Release Notes of vRealize Orchestrator 8.x and is added here just for educational purposes.

Basics of REST

So what is REST? At a high level REST, or REpresentational State Transfer, is an architectural style for distributed hypermedia ystems. It was created from a combination of other architectural styles and enlists several constraints. Roy Fielding, its creator, said that “REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.”

This guide will cover:

  • REST HTTP requests
  • REST HTTP responses
  • Constraints needed for an API to be RESTful


The client has to make a request to the server to get or modify data on the server. A request contains the following:

  • HTTP verb
  • headers
  • path: to a resource
  • [optional] message body: data

Consider the example of a todo list. An example request could look like this:

GET /todos
Accept: application/json

HTTP Methods/Verbs

HTTP methods, or verbs, define what kind of operation to perform on a resource. Some of the primary ones used with RESTful APIs are:

  • GET: get a resource or collection of resources
  • POST: create a new resource
  • PUT: update a resource
  • PATCH: partially modify a resource
  • DELETE: delete a resource

HTTP Headers

The client uses headers to pass along request information to the server. Requests can have an Accept request HTTP header which specifies what content type the client can accept from the server. The values are of the media, or MIME type. The simplest MIME types are of the format type/subtype. For example, this could be text/html which is for a text file containing HTML. Or application/json for a JSON file. To see a list of common Accept values check out the MDN web docs.


Requests need to contain a path to the resource it is trying to perform the HTTP request on. In the above example, that is /todos. This is the resource that you are looking to read from or write to.

One important thing to note is the difference between addressing a collection and an individual item in that collection. The path /todos would be addressing all of the items on the todo list. A POST to this path could create a new item on that list, and a request to GET /todos would return all items. On the other hand, GET /todos/2 would return just the second item on the todo list.


After the client sends a request to the server, the server sends a response back to the client. This response consists of a: status code headers message body: data

For the previous request:

GET /todos
Accept: application/json

The response could contain the following for a successful request:

HTTP/1.1 200 OK
Content-Type: application/json
        "name": "pay rent",
        "due": 1589031653,
        "completed": false
        "name": "get groceries",
        "due": 1588869295,
        "completed": true

HTTP Headers

The responses have a Content-Type entity header which specifies the MIME type of the resource. Its value should match one of the Accept types sent by the client. In the above example, both the Content-Type and Accept header values are application/json.

Headers can contain information on a wide array of topics including how to handle the TCP connection, authentication, caching, and more. Some REST APIs may have headers specific to them, but there’s also some headers that have a universal definition.

Response Status Code

One of the return values in a response is a response status code. Each code has a specific designation; for example, 200 is OK and means the request was successful. The response code categories are as follows:

  • 1XX: Informational
  • 2XX: Successful
  • 3XX: Redirects
  • 4XX: Client Errors
  • 5XX: Server Errors

To see the detailed list of response codes and their meaning, check out the MDN web docs.


The body contains the data the client requested in the MIME type specified in the Content-Type header. In the example above, the body of the response is:

        "name": "pay rent",
        "due": 1589031653,
        "completed": false
        "name": "get groceries",
        "due": 1588869295,
        "completed": true


In order to create RESTful APIs, they need to adhere to six style constraints:

  1. Client-Server Separation
  2. Stateless
  3. Cache
  4. Uniform Interface
  5. Layered System
  6. [Optional] Code-On-Demand


First, there needs to be a separation of client and server. This means that the client and server each need to work independent of each other. Their only form of interaction is when a client makes requests and a server sends a response whenever it receives a request. One of the benefits is that the client and server can each be updated or modified independently without affecting the other.

For example, if you make a call to a restaurant to place a takeout order, the only interaction is you asking for a specific item and them responding with an ok or saying they don’t have it.


Next, the client and server communication needs to be stateless: the server and client don’t need to know the state of the other. The server doesn’t store state from the client and therefore the client can’t depend on it. Therefore the client needs to send everything the server would need to process a request every time and any storing needs to happen client-side.

To continue with the analogy, when you make your call, you don’t need to know what the restaurant has in stock, and they don’t need to know what you want until you order. Additionally, you’re responsible for keeping track of what you like to order, not the restaurant.


Cache constraints require that the response be labeled as cacheable or non-cacheable. If it’s cacheable, then the client can choose to use this data for future requests that are equivalent. For instance, if the data is valid for a set time, the cached data will be used instead of making a request to the server.

For your takeout call, the restaurant may tell you that a specific item is not available until a certain date. You can then remember to not order it again if you make another request before that date.

Uniform Interface

As mentioned, the client and server are independent of each other, meaning they evolve and change independently. For this reason, it’s imperative that the interface between the client and server expect some commonality in their communication. This constraint can actually be broken down even further:

  1. Resource-Based: This is two-fold: First, individual resources should be identifiable in the request. Often this is done in the path of the URI of the request. For example, /todos/2 would directly address the todo item with the ID of 2. Second, the presentation of the resource does not necessarily need to match the internal representation by the server. The todo item may be returned as JSON, but more realistically the server is storing this in a database in another format.
  2. Manipulate Resources Through Representations: When the client receives a resource, the representation of that resource contains enough information to update or delete it. This could be seen as the flip-side of the “stateless” constraint. Using the same example of a todo list, if a client requests all items in a todo list, each of those items would likely include an ID so that it could be individually addressed.
  3. Self-Descriptive Messages: Each message or resource should include enough information so that the client knows how to process that message. For example, if a client has requested a resource that’s returned as JSON, the response should also include a Content-Type header with the value application/json.
  4. Hypermedia as the Engine of Application Sate: AKA “HATEOAS”. This could be a whole conversation on it’s own, and it’s encouraged to read on this topic as well, but in short each response should include related links that the client can use to discover other actions and resources. Continuing the ToDo example, requesting an individual todo item may include links to the list that it is a part of.

Layered System

Layered system constraints are used to scope hierarchical layers based on behavior and have each layer be unable to have visibility past the layer it is interacting with. That’s to say, a client may send a request to a server, which in turn may send a request to a data service, which sends a request to an authentication service. All of this is invisible to the client and the client can not and should not distinguish between a direct request to the data or one that has multiple requests server-side. This is also true for infrastructure and operational components such as proxies and load balancers. Introducing these components to the server architecture should require no updates from the client.

[Optional] Code-On-Demand

This constraint states that a server can extend the functionality of a client by providing it executable code. Probably the most common example of this is client-side scripting with JavaScript, but this can take many forms. While this keeps clients simpler and smaller, it also reduces visibility of features and can introduce ambiguity. Because of this, while the absence of other constraints may mean a service isn’t actually RESTful, this constraint is optional.


Well, this is the end of this coverage of the basics of REST. Reading Roy Fielding’s REST dissertation is a great place to start if you haven’t already.

Working with Date and Time in vRO

Manipulation of Date & Time is overwhelmingly difficult. Mostly because there are so many formats, standards and handling techniques. Being a vRO programmer, you can’t run away from this date manipulation, be it for Reports creation, REST Calls, Data fetching or Interacting with other servers. Ask any programmer about their experience handling dates and time zones and they will probably share some war stories. Handling date and time fields is certainly not rocket science but can often be tedious and error-prone.

In this article, we will explore some of the key concepts that are necessary for manipulating date and time values correctly, formats that are convenient for storing DateTime values and transferring them over APIs, and more. You can consider it as a complete guide to understand the DateTime concepts in vRealize Orchestrator using the system-provided classes.

If you are sending and receiving data through a REST API, you will eventually need to convert the date to a string and vice versa because JSON doesn’t have a native data structure to represent DateTime. 


Intrinsic Date() Class

vRO provides a Date() class to satisfy almost all the date and time hunger. It has a constructor that takes variety of inputs to start off. There are various methods and functions take allows quick shape-shifting of date and time. However, It may lack some quirky features that you may look out for. Let’s start off by taking a look at this Date class in a more comprehensive way.

It starts here. You call the constructor and you will get the current date and time. BOOM!🚀

const currentDate = new Date();

If you don’t pass anything to the Date constructor, the date object returned contains the current date and time.

You can then format it to extract only the date part as follows:

const currentDate = new Date();
const currentDayOfMonth = currentDate.getDate();
const currentMonth = currentDate.getMonth(); // Be careful! January is 0, not 1
const currentYear = currentDate.getFullYear();
const dateString = currentDayOfMonth + "-" + (currentMonth + 1) + "-" + currentYear;
//Output = "19-03-2022"

Caution It’s not getYear(), but getFullYear()

If you instead want to get the current time stamp, you can create a new Date object and use the getTime() method.

const currentDate = new Date();
const timestamp = currentDate.getTime();
System.log(timestamp); // 1647678776796

Tip In JavaScript, a time stamp is the number of milliseconds that have passed since January 1, 1970, which is also known as Unix/ECMAScript Epoch format.

You can also take an input of Type Date in workflow or action and handle it in your scripts just like any other Date object.

Parsing a Date

Converting a string to a JavaScript date object is done in different ways.

The Date object’s constructor accepts a wide variety of date formats:

const date1 = new Date("Wed, 27 July 2022 13:30:00");
const date2 = new Date("Wed, 27 July 2022 07:45:00 UTC");
const date3 = new Date("27 July 2022 13:30:00 UTC+05:30");

or we can also use Date.parse(), that will return timestamp as string

var ms = Date.parse("27 July 2022 13:30:00 UTC+05:30"); 
System.log(ms); // 1469605500000 (timestamp)

Note that you do not need to include the day of week because JS can determine the day of the week for any date.

You can also pass in the year, month, day, hours, minutes, and seconds as separate arguments:

const date = new Date(2022, 6, 27, 13, 30, 0);

or you can even pass the Unix Epoch number directly:

const date = new Date(1647678776796);

that means to get the zeroth timestamp i.e. Jan 1st of 1970 UTC+0, pass 0 as a parameter to Date()

const date = new Date(0);

or you can also use System.getDateFromFormat() to convert a string to Date object. Check more here.

d = System.getDateFromFormat("2019-01-01T01:45:00.100", "yyyy-MM-dd'T'HH:mm:ss.SSS");

Working with ISO 8601 Format (YYYY-MM-DDTHH:mm:ss.SSSZ)

Of course, you can always use this specific ISO date format:

const date = new Date("2022-07-27T07:45:00.000Z"); // Fri Sep 02 2022 07:45:00 GMT-0000 (GMT)

Get Current Date in ISO complete format

Many a times, we need Dates in a complete ISO format [YYYY-MM-DDTHH:mm:ss.SSSZ], for making REST calls etc. We can use the toISOString() or toJSON() methods of the Date object to convert the local DateTime to UTC.

const dateFromUI = "12-13-2022";
const timeFromUI = "15:20";
const dateParts = dateFromUI.split("-");
const timeParts = timeFromUI.split(":");
const date = new Date(dateParts[2], dateParts[0]-1, dateParts[1], timeParts[0], timeParts[1]);
const dateISO = date.toISOString();
System.log(dateISO); //2022-12-13T15:20:00.000Z

Caution Not all variations of ISO 8601 format are supported by Date() constructor in vRO.

const date = new Date("2022-07-27T07:45:00Z"); // Invalid Date

If you are sure of the format you want to use, it is best to extract individual bits using the JavaScript functions we covered above and create a string yourself.

var currentDate = new Date();
var date = currentDate.getDate();
var month = currentDate.getMonth(); 
var year = currentDate.getFullYear();

We can get the date in MM/DD/YYYY format as

var monthDateYear  = (month+1) + "/" + date + "/" + year;

The problem with this solution is that it can give an inconsistent length to the dates because some months and days of the month are single-digit and others double-digit. This can be problematic, for example, if you are displaying the date in a table column, because the dates don’t line up.

We can address this by using a “pad” function that adds a leading 0.

function pad(n) {
    return n<10 ? '0'+n : n;

Now, we get the correct date in MM/DD/YYYY format using:

var mmddyyyy = pad(month + 1) + "/" + pad(date) + "/" + year;

If we want DD-MM-YYYY instead, the process is similar:

var ddmmyyyy = pad(date) + "-" + pad(month + 1) + "-" + year;

Let’s up the ante and try to print the date in “Month Date, Year” format. We will need a mapping of month indexes to names:

var monthNames = [
    "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
var dateWithFullMonthName = monthNames[month] + " " + pad(date) + ", " + year;

It is easy to determine the day of week from 0 (Sunday) to 6 (Saturday). The first day is always Sunday, so let’s add that in:

var daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
//var completeDaysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
ordinalDateWithDayOfWeek = daysOfWeek[currentDate.getDay()] + ", " + ordinalDate;

By now, you might understand how to get bits of information out of dates and how to pad them. Now, let’s create an ISO format from scratch like I have done here (contains +00:00 instead of Z as per my requirement).

function getCurrentDateInISOFormat() {
//Desired Format 2016-06-14T00:00:00.000+03:00
var date = new Date();
System.log("Current Date: " + date.toGMTString());
var yyyy = date.getFullYear();
var mm = (date.getMonth() + 1 < 10 ? "0" : "") + (date.getMonth() + 1);
var dd = (date.getDate() < 10 ? "0" : "") + date.getDate();
var HH = (date.getHours() < 10 ? "0" : "") + date.getHours();
var MM = (date.getMinutes() < 10 ? "0" : "") + date.getMinutes();
var SS = (date.getSeconds() < 10 ? "0" : "") + date.getSeconds();
var milli = "";
if (date.getMilliseconds() < 10)
milli = "00" + date.getMilliseconds();
else if (date.getMilliseconds() < 100 && date.getMilliseconds() > 10)
milli = "0" + date.getMilliseconds();
milli = date.getMilliseconds();
System.log(yyyy + "-" + mm + "-" + dd + "T" + HH + ":" + MM + ":" + SS + "." + milli + "+00:00");
return (yyyy + "-" + mm + "-" + dd + "T" + HH + ":" + MM + ":" + SS + "." + milli + "+00:00");

Get the number of seconds since the Unix/ECMAScript Epoch

var seconds = Math.floor(Date.now() / 1000);

Working with past and future dates

The best way to work and calculate present and future dates is by using Unix Epoch format which is conveniently the number of milliseconds after midnight January 1, 1970 till the given date expressed as a string which is IETF format. Let’s see few examples.

Important It should be noted that the maximum Date is not of the same value as the maximum safe integer (Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991). Instead, it is defined in ECMA-262 that a maximum of ±100,000,000 (one hundred million) days relative to January 1, 1970 UTC (that is, April 20, 271821 BCE ~ September 13, 275760 CE) can be represented by the standard Date object (equivalent to ±8,640,000,000,000,000 milliseconds).

Get current time in milliseconds

// vRO System method
System.getCurrentTime() //1647861957381 
//Javascript method
Date.now() //1647861957381 
var date = new Date(); //allows any Date to be used 
System.log(date.valueOf()); //1647861957381

Lets say you want to fetch the date 4 days later relative to today, you can convert the today’s date in Unix Epoch format and add 4 x 24 x 60 x 60 x 1000 milliseconds to it and you will get a date exactly 4 days ahead with same time of the day, that because you have not changed enough milliseconds to modify the time.

var date = new Date(); //Thu Mar 21 2022 11:42:06 GMT-0000 (GMT)
var frameOfTime = date.valueOf() + (4*24*60*60*1000);
var date = new Date(frameOfTime); //Thu Mar 25 2022 11:42:06 GMT-0000 (GMT)

Now, let’s say you want to go back in time 4 hours back, You will subtract 4 x 60 x 60 x 1000 milliseconds.

var date = new Date(); //Thu Mar 21 2022 11:42:06 GMT-0000 (GMT)
var frameOfTime = date.valueOf() - (4*60*60*1000);
var date = new Date(frameOfTime); //Thu Mar 25 2022 07:42:06 GMT-0000 (GMT)

Comparing Dates

First, we need to create date objects. Fortunately, <, >, <=, and >= all work. So comparing July 19, 2014 and July 18, 2014 is as easy as:

const date1 = new Date("July 19, 2022");
const date2 = new Date("July 28, 2022");
if(date1 > date2) {
    System.log("First date is more recent");
} else {
    System.log("Second date is more recent");

Checking for equality is trickier, since two date objects representing the same date are still two different date objects and will not be equal. Comparing date strings is a bad idea because, for example, “July 20, 2022” and “20 July 2022” represent the same date but have different string representations. The snippet below illustrates the first point:

const date1 = new Date("June 10, 2003");
const date2 = new Date(date1);
const equalOrNot = date1 == date2 ? "equal" : "not equal";

This will output not equal.

This particular case can be fixed by comparing the integer equivalents of the dates (their time stamps) as follows:

date1.getTime() == date2.getTime();

Moreover, vRO is not very good with timezones. So, the best is that we should ignore the user’s time zone and use UTC while creating the date object. There are two ways to do it:

  • Create an ISO formatted date string from the user input date and use it to create a Date object. Using a valid ISO date format to create a Date object while making the intent of UTC vs local very clear.
const userEnteredDate = "12/20/1989";
const parts = userEnteredDate.split("/");
const userEnteredDateISO = parts[2] + "-" + parts[0] + "-" + parts[1];
const userEnteredDateObj = new Date(userEnteredDateISO + "T00:00:00.000Z");
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDateObj.getTime() == dateFromAPI.getTime(); // true

This also works if you don’t specify the time since that will default to midnight (i.e., 00:00:00Z):

const userEnteredDate = new Date("1989-12-20");
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true

Remember: If the date constructor is passed a string in correct ISO date format of YYYY-MM-DD, it assumes UTC automatically.

  • JavaScript provides a neat Date.UTC() function that you can use to get the UTC time stamp of a date. We extract the components from the date and pass them to the function.
const userEnteredDate = new Date("12/20/1989");
const userEnteredDateTimeStamp = Date.UTC(userEnteredDate.getFullYear(), userEnteredDate.getMonth(), userEnteredDate.getDate(), 0, 0, 0);
const dateFromAPI = new Date("1989-12-20T00:00:00.000Z");
const result = userEnteredDateTimeStamp == dateFromAPI.getTime();
System.log(result); //true

Finding the Difference Between Two Dates

A common scenario you will come across is to find the difference between two dates.

We discuss two use cases:


Convert both dates to UTC time stamp, find the difference in milliseconds and find the equivalent days.

const dateFromAPI = "2022-02-10T00:00:00.000Z";
const now = new Date();
const datefromAPITimeStamp = (new Date(dateFromAPI)).getTime();
const nowTimeStamp = now.getTime();
const microSecondsDiff = Math.abs(datefromAPITimeStamp - nowTimeStamp);
// Math.round is used instead of Math.floor to account for certain DST cases
// Number of milliseconds per day =
//   24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second
const daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60  * 24));
System.log(daysDiff); //2231


Note: We have a non-standard format. Read the API doc to determine if this means 12 Oct or 10 Dec. Change to ISO format accordingly.

const birthDateFromAPI = "12/10/1989";
const parts = birthDateFromAPI.split("/");
const birthDateISO = parts[2] + "-" + parts[0] + "-" + parts[1];
const birthDate =  new Date(birthDateISO);
const today = new Date();
var age = today.getFullYear() - birthDate.getFullYear();
if(today.getMonth() < birthDate.getMonth()) {
if(today.getMonth() == birthDate.getMonth() && today.getDate() < birthDate.getDate()) {

Find Execution time of a f(n)

You can use these logics to get more purposeful result. You can calculate the execution time of a function you just created and may optimize it.

// To test a function and get back its return
function printElapsedTime(fTest) {
var StartTime = Date.now(),
vReturn = fTest(),
EndTime = Date.now(),
difference = EndTimeStartTime
System.log("Elapsed time: "+difference+" milliseconds") //5001 milliseconds
return vReturn
var yourFunctionReturn = printElapsedTime(sort)
function sort(){ // your function
const array1 = [1, 30, 4, 21, 100000];


vRO does provide various methods to represent date and time in various formats out-of-the-box. Let’s have a look on their output.

var date = new Date(Date.UTC()); //Mon Jan 01 1900 00:00:00 GMT-0000 (GMT)
System.log(date.toString()); //Fri Aug 23 1999 14:53:51 GMT-0000 (GMT)
System.log(date.toTimeString()); //14:53:51 GMT-0000 (GMT)
System.log(date.toLocaleTimeString()); //2:53:51 PM GMT
System.log(date.toLocaleDateString()); //January 6, 2009
System.log(date.toDateString()); //Tue Jan 06 2009
System.log(date.valueOf()); //1455062400000
System.log(date.toUTCString()); // Wed, 10 Feb 2016 00:00:00 GMT


  • Date and time in JavaScript are represented with the Date object. We can’t create “only date” or “only time”: Date objects always carry both.
  • Months are counted from zero (yes, January is a zero month).
  • Days of week in getDay() are also counted from zero (that’s Sunday).
  • Dates can be subtracted, giving their difference in milliseconds. That’s because a Date becomes the timestamp when converted to a number.
  • Use Date.now() to get the current timestamp fast.


List of available Node.js modules in vRO

Here is the list of node.js modules that comes preinstalled in vRO 8.3 when you select in Node.js 12 in Runtime.

Note: http_parser is removed from later versions of vRO Node.js Runtime

Reference Script

In Node.js, the process.versions property is an inbuilt application programming interface of the process module which is used to get the versions of node.js modules and it’s dependencies. The process.versions property returns an object listing the version strings of Node.js and its dependencies.

exports.handler = (context, inputs, callback) => {
const process = require('process');
var no_versions = 0;
var versions = process.versions;
for (var key in versions) {
// Printing key and its versions
console.log("|"+key + "|" + versions[key]+"|");
console.log("Total no of values available = " + no_versions);
callback(undefined, {
status: "done"

Let me know if you have an even better way to get the list of modules from vRO. See you.

Build a Notification bot with Google Chat for vRealize Orchestrator

Important To use bots in Google Chat, you must use a work or school account.


How about building a Chatbot that brings your vRealize Orchestrator services and resources right into Google Chat, letting users get vital bits of information without leaving the conversation. That would be great, right? Why Chat you may ask if you already have Gmail? I would say because Chat is more intuitive, simple, integrated and fun-to-watch😉. And it doesn’t require any SMTP configuration to be done in vRO.

Potential Use-cases

  • Notifications of successful Workflow Executions on Google Chat Team Spaces
  • Error Notifications and auto-assignments to team member using @mention
  • Daily Reports and Validations on Team Spaces
  • and so many possibilities…

Using Incoming webhooks

Incoming webhooks let you send asynchronous messages into Google Chat from applications that aren’t bots themselves. For example, you can configure vRealize Orchestrator to notify personnel on Google Chat when a particular server goes down. Incoming webhooks serve as a quick and easy way to integrate with existing workflows such as these without the overhead of writing entire bot applications. This is the path for low-effort integrations that provide information feeds or alerts. Let’s dive into the whole process of building a chatbot using webhooks that triggers via vRealize Orchestrator.

Step 1: Register the incoming webhook

  • Open Google Chat in your browser.
  • Create a new space or Go to the space to which you want to add a bot.
  • From the menu at the top of the page, select Manage webhooks.
  • Under Incoming Webhooks, name the new webhook ‘vRO Notifications’ and provide an avatar URL, click SAVE.

Tip Get the 128x128px avatar for your bot here.

Avatar URL: https://user-images.githubusercontent.com/7029361/157434341-d1cd1706-4072-4f32-abe1-2f0459e93879.png

  • Copy the URL listed next to your new webhook and keep it handy. We will use it later on.
  • Click outside the dialog box to close.

Step 2: Import the Node.js script action in vRO

  • Download the vRO package from here.
  • Import this package in your vRO.
  • Check action named sendNotificationsToGoogleChat. This action will be used to send notifications to Google Chat.
  • Run the action and pass webhookURL without https://chat.googleapis.com and a test message. (If you need to find the URL again, go to the Google Chat space, select Manage webhooks, and then copy the URL associated with your incoming webhook.)

If everything is OK, you should see this message in your space.

Step 3: Use sendNotificationsToGoogleChat action in Workflows

To utilize this action, we have to attach it in workflows for automatic notifications. Let’s take a scenario where we want to notify the team when the workflow is failed.

  • Create a new workflow Demo Workflow – Chatbot and add a scriptable task that will fail.
  • Add a Default error handler item and an scriptable task and the action sendNotificationsToGoogleChat.
  • In this new scriptable task, copy-paste this JS script with webhookPath that you have copied from your space.
webhookPath = "/v1/spaces/........";
message = "Important message for <users/all>: The workflow *";
message += workflow.rootWorkflow.name;
message += "* has been failed";
  • Add a error variable and add it to Exception Handling in all the workflow items
  • Time to run, Click Run and wait for it to fail.
  • Check your space. There should receive a message.

Tip Find this workflow in the package for testing purposes.

Limits and considerations

As you configure webhooks, take note of these limits and considerations:

  • If only select organizational units (OUs) in your domain have bots enabled, incoming webhooks created in a space will fail with the following error:
  "error": {
    "code": 403,
    "message": "The caller does not have permission",
    "status": "PERMISSION_DENIED"
  • Incoming webhooks do work in direct messages, but only when all users have bots enabled.
  • Because Manage webhooks is only available from a web browser, webhooks must be set up from the Chat web app. Webhooks are not configurable from the Chat mobile app.

Text Message Formatting Guide

This section describes how to create simple messages that appear inline as if typed by a user. The is basically the message that we are passing to our vRO action.

Basic unformatted messages

Any message in Google Chat is represented as a JSON object. A basic message that specifies just a simple plaintext body could look like the following:

{  'text': 'Your workflow "Create Egde Gateway" has successfully executed'}

If posted into Google Chat (e.g. via incoming webhooks), it would look like the following:

Using formatted text in messages

Google Chat lets you add basic formatting to the message text, including bold, italic, and strikethrough. To format text, wrap it with the following symbols:

Italic_ (underscore)_hello_hello
Monospace` (backquote)`hello`hello
Monospace block“` (three backquotes)“`

For example, consider the following JSON:

{'text' : 'Your workflow *has successfully executed*!\nCustomer created with id: _A21347-VPC_'}

This places a message like this into the Chat space:

If you include a plain link URL in your message text, such as http://example.com/foo, Google Chat uses this as the link text and automatically hyperlinks that text to the specified URL.

To provide alternate link text for your link, use the following syntax:

<https://cloudblogger.co.in|my link text>my link text

The pipe and link text are optional, so that <https://example.com/foo> and https://example.com/foo are equivalent.

Messages that @mention specific users

A bot can @mention a user in a message, providing the user ID in the following syntax:


This string will be substituted with an @mention of the specified user. For example, consider the following JSON:

{"text": "Hello <users/113352777658254482749> from vRO!}

This payload would produce a result like the following:

To determine the USER_ID for a user, In Google Chrome, Click

and Click Inspect on the chat page, and point out to the user,

you will get the USER-ID as data-member-id as shown here.

Important Change user/human/113352777658254482749 to users/113352777658254482749 while pasting it into your code.

Messages that @mention all users

You can use the special user ID all to @mention all the human users in a space. For example:

{    "text": "Important message for <users/all>: Code freeze starts at midnight tonight!"}

Coming Soon -> Card messages

Google Chat supports more complex UIs in the form of cards that can give much more insights of our infrastructure.

A simple example card:

List of all available Python modules in vRO 8.x

List of all Python 3.7 modules that comes preinstalled in vRealize Orchestrator 8.x for quick reference:


Tip Run this script in a python-based vRO action to get your own list.

def handler(context, inputs):
	return None

Import a Certificate to the Orchestrator Trust Store using PEM-encoded file

vRealize Orchestrator server must be able to verify their identity, to communicate with other servers (vCenter Server, relational database management system (RDBMS), LDAP, Single Sign-On, and other servers) securely. For this purpose, you might need to import the SSL certificate of the remote entity to the Orchestrator trust store.

To trust a certificate, you can import it to the trust store either by establishing a connection to a specific URL, or directly as a PEM-encoded file.

Import from URL or
proxy URL
The URL of the remote server:
https://your_server_IP_address or your_server_IP_address:port
Import from filePath to the PEM-encoded certificate file.

In this post, we will focus on how to import a certificate using PEM-encoded file. For URL based import, simply use the Library Workflows.

Step 1: Generate PEM file


Find the fully qualified domain name of the server to which you want Orchestrator to connect over SSL.


  • Log in to the Orchestrator Appliance over SSH as root.
  • Run a command to retrieve the certificate of the remote server.
openssl s_client -connect host_or_dns_name:secure_port
  1. a If you use a nonencrypted port, use starttls and the required protocol with the openssl command.
openssl s_client -connect host_or_dns_name:port -starttls smtp
  • Copy the text from the -----BEGIN CERTIFICATE----- to the -----END CERTIFICATE----- tag to a text editor and save it as a file in your local system.

Step 2: Import a PEM-encoded file

We can use either of the following ways for this purpose.

  • Control Center
  • vRO Library Workflow

via Control Center

  • Log in to Control Center as root.
  • Go to the Certificates page.
  • On the Trusted Certificates tab, click Import and select the Import from a PEM-encoded file option.
  • Browse to the certificate file and click Import.

via vRO Workflow

  • Log in to vRO Portal.
  • Go to Workflows under Library > Configuration > SSL Trust Manager.
  • Run workflow Import a trusted certificate from a file
  • Browse to the certificate file and click Run.
  • The workflow should be successfully completed.


You have successfully imported a remote server certificate to the Orchestrator trust store. You can check an entry for the corresponding server in the Control Center Certificates Tab or try running the workflow related to the server for which the certificate was imported. You won’t see this error anymore.

A brief investigation on vRO const

  1. Introduction
  2. Rhino 1.7R4 Const Feature Compatibility Chart
  3. What is const?
  4. Typical Properties of a const
  5. How does a vRO const differ?
  6. Test Results
    1. Error: Assignment to constant variable [FAIL]
    2. Object.freeze() [PASS]*
    3. Name-sharing with f(), variable or other const in the same scope [PASS]
    4. const needs to be initialized [FAIL]
    5. const in objects and arrays [FAIL]
    6. const in for-in loop [FAIL]
  7. Suggestive Readings
  8. References


vRealize Orchestrator leverages Mozilla Rhino Engine 1.7R4 as its scripting engine and implemented Javascript 5.1 that allows developer to create automation workflows/actions as a collection of Javascript scripts.

However, there are evidences that the JS constructs used in vRO 8.x are not confined to Javascript 5.1, for example, const. Let’s talk about it.

As you notice at Ecma-262 Edition 5.1 website, there is no const keyword.

const keyword was introduced in ES2015 (ES6) to define a new variable.

Looking at it, I can say I am not very sure which version of JavaScript vRO exactly uses, It is probably somewhere in between 5.1 and 6.0 as it doesn’t support all 6.0 features.

Rhino 1.7R4 Const Feature Compatibility Chart

This chart clearly shows that Rhino Engine in vRO only provides a basic support for const. However, we will do some tests to validate it.

Source: https://mozilla.github.io/rhino/compat/engines.html

What is const?

const or Constants are block-scoped variables declared using the const keyword. The value of a constant can’t be changed through reassignment (i.e. by using the assignment operator), and it can’t be redeclared (i.e. through a variable declaration). However, if a constant is an object or array its properties or items can be updated or removed.

Typical Properties of a const

  • Cannot be reassigned
  • Block Scope
  • It can be assign on the variable on declaration line.
  • Primitive value.
  • The property of a const object can be change but it cannot be change to reference to the new object
  • The values inside the const array can be change, it can add new items to const arrays but it cannot reference to a new array.
  • Re-declaring of a const variable inside different block scope is allowed.
  • Cannot be Hoisted.
  • Create only read only reference to value.

How does a vRO const differ?

Now, I have also realized that even if const is supported in vRO, it doesn’t behave exactly like the normal const. So, I have done some testing by executing the same code in vRO 8.3 and on the Mozilla website here and compared the results. Let’s see which use-case passed the test and which one failed.

Test Results

Error: Assignment to constant variable [FAIL]

Object.freeze() [PASS]*

* vRO does freeze the Object but no TypeError

Name-sharing with f(), variable or other const in the same scope [PASS]

const needs to be initialized [FAIL]

const in objects and arrays [FAIL]

const in for-in loop [FAIL]

With all these outcomes, it’s clear that vRO JS Engine is a totally different entity and it has its own set of rules. We just have to find them. If you like this article, feel free to share and comment. See you on other posts.

Suggestive Readings

  1. Javascript Limitations in vRealize Orchestrator 8.6 by Joey Kleinsorge
  2. JavaScript ES6+: var, let, or const? by Eric Elliott