With the release of VMware vRealize Orchestrator 8.1 in April 2020, VMware added the ability to utilize new languages within your workflows and actions. These new languages included PowerShell, Node.js, and Python. Since then, I have seen very few documents detailing the use of this capability.
Recently though, while attempting to migrate some legacy scripts from a vRealize Orchestrator 7.6 deployment to vRealize Orchestrator 8.8, I found a need to implement DNS lookup capabilities within a workflow that previously utilized nslookup commands within the Orchestrator appliance. Due to the containerized nature of vRealize Orchestrator 8.8, the nslookup command no longer functioned. After digging within the Node.js documentation for a possible solution, I ran across the Node.js DNS library. This library allows for several types of DNS queries, including utilizing operating system methods or specifying specific DNS servers.
In this post, I provide a walkthrough of what I learned during my journey to utilize Node.js and examples of how to implement DNS querying functionality using Node.js within Aria Orchestrator 8.10.
While Aria Automation Orchestrator does include a function to resolve DNS queries utilizing the Orchestrator server’s built-in DNS, this method did not provide me with the ability to specify specific DNS resolvers. In my particular use case, I needed the ability to set a specific DNS resolver not utilized by the Orchestrator appliance.
I don’t have a concrete reason for this outside of the following:
Based on these reasons, the best path forward would be to explore implementing the solution using Node.js. My next task was to determine how exactly I’d do that.
The first step to learning and testing Node.js in Aria Automation Orchestrator is creating a new workflow containing a single Scriptable task object. To do this, we create a new workflow with no inputs or outputs and add the Scriptable task object. Next, under the Scripting tab for the Scriptable task, the default Runtime Environment will be listed as “JavaScript”. We change this to “Node.js 14”. After making this change, notice that content has been added to the scripting object’s script block.
The newly added text is an example script that includes the minimum required content to utilize Node.js in Aria Automation Orchestrator. The script provided is as follows:
exports.handler = (context, inputs, callback) => {
console.log('Inputs were ' + JSON.stringify(inputs));
callback(undefined, {status: "done"});
}
If you are unfamiliar with JavaScript, I will explain the above code. The first line of the code defines a new function called handler. This function is passed three inputs contained within the parentheses that Aria Automation Orchestrator provides:
Finally, the contents of the handler function are indicated by the “=>” and are contained within the curly brackets. The second line of the code logs to the console the string “Inputs were ” and concatenates the inputs object that has been converted to a string. The third line calls the callback function that was passed into our function with a return object that contains a variable called status with a value of “done”.
If we save the workflow as is and execute it, we see that the workflow log will show the string “Inputs were {}” which matches our Node.js function’s intention.
Now that we have the basics let’s define the workflow input values that our scriptable task will use to complete a DNS lookup of a hostname using one or more defined DNS servers. To do this, we define a hostname input of type string, a dnsServers input array of type string, and a results output array of type string. Again, we save the workflow and execute it. This time we are prompted to provide values for our new inputs. In this case, we set the hostname value to “google.com” and then add a value to the dnsServers input of “8.8.8.8”. We haven’t yet added any code to complete a DNS lookup, but the workflow log will now show our input values in the log as a set of name/value pairs in JSON.
Now that we have verified that our Node.js scriptable task inputs are available to use in our script, let’s add our DNS lookup logic into the script and output our results to the console. The new code is the following:
exports.handler = (context, inputs, callback) => {
const { Resolver } = require('dns');
const resolver = new Resolver();
console.log('Querying DNS results for: %j', inputs.hostname);
resolver.setServers(inputs.dnsServers);
resolver.resolve(inputs.hostname, (err, addresses) => {
console.log('Results: %j', addresses);
callback(undefined, {status: "done", results: addresses});
});
}
There’s a lot more happening in our script, so I will explain. We already discussed the first line of the script, so we’ll move on.
The second line of the script is importing the Node.js module called dns. This module contains the functions necessary to complete our DNS lookups.
The third line defines a new constant variable containing an instance of the Node.js dns resolver class.
The fourth line logs our first input, which is the hostname input. Notice that this value is referenced utilizing the inputs variable in the format of inputs.{input name}.
The fifth line relates to the Node.js dns module, and it assigns our list of DNS servers as the servers that should be used by the resolver object when looking up our hostname. The setServers function takes an array of type string as its only parameter.
The sixth line calls the resolve function of the resolver. This function takes our hostname as the first parameter and defines a new callback function with two parameters that will be executed when the result is obtained from the network. The first parameter err contains error information if the function execution encounters an error. The second parameter addresses will contain the IP addresses resolved for the hostname. This new callback function contains two lines of code. The first logs the results obtained to the console, while the second calls our scriptable task’s callback function with no error state and two outputs. The outputs are status with a value of “done” and addresses that contain the array results of our DNS lookup. Let’s execute this workflow again using the same inputs of “google.com” and “8.8.8.8” to see our results. Notice that our workflow log now displays two log entries showing the hostname we are looking up and an array of IP addresses resolved for the hostname.
Additionally, if we review our workflow variables, we see that the results variable has been assigned an array of outputs that contains the IP addresses returned by our Node.js script.
One thing to understand when working for Node.js/JavaScript is the concept around asynchronous execution. In the case of our DNS query, the results take time to come back. Instead, the resolver is provided a set of code (a function) to execute on the results once they are received back from the network. This allows us to control what happens with the results and controls the execution flow of the function. In the case of our Node.js scriptable task, we are passed a callback function from Aria Automation Orchestrator that we call from our code to indicate that we are done executing our code and providing the results.
Our DNS code works great in our workflow, but the capability provided is something we would like to use repeatedly in several workflows. This makes it a great use case for defining an Aria Automation Orchestrator action. Moving our code over to a new action would look like the following:
Notice that we have a couple of fields labeled Memory limit and Timeout. While these values can be left as is in most cases, there may be times when you import multiple dependency modules where 64 MB of memory may not be enough. In those situations, defining your code in an action will allow you to fine-tune these values to enable the successful execution of your code.
Now that we have defined our action let’s go back to our workflow and swap out our scriptable task with our new action. We assign the same inputs and outputs to the action and test executing our workflow. Whoops! It seems that things are now broken! If you look through the workflow log, you will see that our output is assigned a value of null, which is not an array of type string.
Why would this be happening? If you think about it, an Aria Automation Orchestrator action does not allow the return of multiple values as an object. Aria Automation Orchestrator actions only allow for a single returned value. In our code, we are attempting to return a string and an array. Let’s revisit our action and change it to only return our addresses array. Our action’s code is now defined as the following:
exports.handler = (context, inputs, callback) => {
const { Resolver } = require('dns');
const resolver = new Resolver();
console.log('Querying DNS results for: %j', inputs.hostname);
resolver.setServers(inputs.dnsServers);
resolver.resolve(inputs.hostname, (err, addresses) => {
console.log('Results: %j', addresses);
callback(undefined, addresses);
});
}
Now, when our workflow executes, we receive our DNS lookup results, which are assigned to our results output variable.
While not as straightforward as working with JavaScript code native to Aria Automation Orchestrator, utilizing Node.js scripts within our workflows and actions isn’t too difficult. The main difference is the additional supporting code encapsulating our functions and handling input/output variables.
Search
Get Notified of Future Posts
Recent Posts