Workflowing with VUM and VCO – The Basics

If you’re using VCO/VRO as your automation engine, sooner or later you will probably add the vSphere Update Manager plugin.
Makes sense, right? The opportunity to schedule or delegate the ability to update and patch your hosts is pretty compelling.

It’s unfortunate that getting up and running with VUM isn’t quite as simple as it looks on the surface. In this particular case, PowerCLI  wins for its ease of integration.
Hopefully, this post helps turn that tide back.

It’s a little bit behind

The installation of the plugin is just like any other – upload it from the Configuration interface on port 8283. I don’t think it really even needs a reboot.
From there, you have to punch in your vCenter URL – which may not be obvious to many as there is little help or documentation.

So just to be clear, add the URL like this : 
https://myvcenterserver.lab.local/sdk

You can also add others in the list if you have multiple vCenters in the same authentication scope in a similar way.

Next up, check your plugin status within the VCO client. Inevitably you will run into an error that simply says ERROR IN PLUGIN.
Unless you are the white knight of IT, this isn’t too helpful.
If you see this, I’m willing to bet that it’s because you didn’t import the SSL certificate to the trusted store.
How would you know to do that? You wouldn’t, unless you like staring at logs set to DEBUG level!

So, how do I import the certificate?
Easy – just point VCO to the SOAP endpoint of your VUM box. You can get the service port piece of this information from the Admin section of the Update Manager plugin through the vSphere Client. You can do this through the Configurator page too, but since Configurator is going away, this is probably the best way.

Locating the SOAP port for VUM.
Locating the SOAP port for VUM.

Now, you can run a workflow to import the VUM certificate into the SSL Trust Store.
You can find the builtin workflow in Library->Configuration->SSL Trust Manager.
Choose to run the Import a certificate from URL workflow.

The Import Certificate workflow.
The Import Certificate workflow.

Execute the workflow, and for the input, use the FQDN to the server running the VUM service, and append port 8084 to the end, like you saw earlier.
The FQDN portion is important! If you don’t put it in there, you will likely have an error.

Importing the VUM SSL Certificate.
Importing the VUM SSL Certificate.

Once the certificates are imported, relaunch your VCO client. After you login, you should see some progress being made.

It's ALIVE!
It’s ALIVE!

That wasn’t so hard

So next up, you just bind a HostSystem value to a workflow that remediates and you’re good right?
Unfortunately not quite yet. But we’ll get there!

VUM uses a completely different service and object model unrelated to vCenter, thus direct integration is not as simple. Out of the box, you have to write your own code and do some heavy debugging in the logs.

Connecting the dots

The first thing we will do is make a simple Action element that takes an input of type VC:HostSystem and extracts the necessary info out of it to create its VUM-equivalent object.

Digging into the API Explorer, look for the VUM:VIInventory Scriptable Action type. This will tell you what you need in order to construct the corresponding object.

The VUM:VIInventory Type.
The VUM:VIInventory Type.

Thankfully, this is a pretty simple type to instantiate – it only requires 4 arguments to construct.
VumVIInventory(string): The string to input here is the vCenter Server.
id: This is the “vSphere Object ID” – essentially the Managed Object Reference ID.
name: This is the host object name in vCenter, whether it’s the FQDN or if you have it connected by IP. (Shame on you!)
type: This is the asset type. Keep in mind, VUM has the ability to patch Virtual Appliances too, so specifying this is required.

So, let’s get some code in place to make the VC -> VUM conversion happen! Create a new Action, and use the below setup as your guide.

Setting up the conversion VCO Action.
Setting up the conversion VCO Action.

Wait, why is the return type an Array?
The VUM plugin expects an array of VUM:VIInventory, even if that means it is an array of one object.

Here’s the Action code to use, with some notes.

// create the new VUM Inventory object and assign values.
// first define a new instance of the object, pointing to the vCenter Server of the host
var vumItem = new VumVIInventory(inputHostSystem.sdkConnection.name);

// get the Managed Object Reference
vumItem.id = inputHostSystem.reference.value;
// get the Managed Object Type
vumItem.type = inputHostSystem.vimType;
// get the ESXi Host name
vumItem.name = inputHostSystem.name;

// the VUMVIInventory object must be an array, so create a dummy one and push the host value in
var vumHosts = [];
vumHosts.push(vumItem);

// return the array of VUM objects (even if it is just one)
return vumHosts;

With this new instance of a VUM:VIInventory type, you can bind it to a Remediate Host workflow as you normally would for your patching pleasure, right?
Theoretically, yes. But you may want to check something else before celebrating.

java.lang.NullPointerException -or- Lessons in Error-Checking

One thing that you will want to verify prior to attempting to remediate using the VCO plugin is whether or not a set VUM Baselines are attached.
If you have no baselines attached, or not specified, your Remediate workflow will error out and throw you a generally unhelpful message.
Here’s how you can check for, attach, and bind the necessary data to the ESX host you want to remediate.

There is an Action as part of the plugin that will attach Baselines to a host object, but you have to tell it which ones you want. Below is sample code you can use in a Scriptable Task (or Action) to output a list of Baselines in your vCenter Server. Since you may have specific Baselines you wish to use, you’ll have to modify it to your liking – but it should be pretty easy.

The input binding on this task can be any object – for this example, it is the ESX host.

// create a search query for VUM baselines whose name begins with "SuperCool"

// query all baselines on the VM Host's vCenter Server
var searchSpec = new VumBaselineSearchSpec(inHost.sdkConnection.name) ;
// define regex to search baselines
var expression = "^SuperCool"

// get the list of baselines
var baselineList = VumObjectManager.getFilteredBaselines(searchSpec)
// VumBaseline must be an array, so make a dummy one
var baselineArray = []

// Loop through the findings and if they match, add to the baseline list.
for each(baseline in baselineList) {
  if(baseline.name.match(expression)) {
    System.log("Baseline: "+baseline.name)
    baselineArray.push(baseline)
  }
}

// assign the array values to the output binding, which is 'vumBaselines'
// if this were an Action, you would just return 'baselineArray'
vumBaselines = baselineArray

So after this, you’ll have your hosts to Remediate, and the Baselines you wish to use for the Remediation. Simply bind these arrays to the inputs of the Remediate workflow, and you’re off to the races!

As an aside, I’m hopeful the next iteration of the plugin, along with the 6.x release with VUM integrated into the VCSA will make life simple for us Orchestrator types. We will see!

VMware VSA Deepdive – Part 4 – Shutting Down VSA (SSH Edition)

The first way I elected to try and forcibly shut down the VSA VM was to do everything through the ESXi command line via SSH. ESXCLI itself is not implemented in VCO directly, so this will require some good old fashioned text parsing with AWK.

Enabling SSH on the host through a VCO Action

Unfortunately out of the box, there is no workflow/action that manages ESXi services, so I needed to roll my own.
Below is the Action setup and script code I used to check for the SSH service and start it up, given an input of type VC:HostSystem.
Create the Action, and name it startSSHService. There is no return value necessary on this Action.
Setting up the SSH Service Action

// get the list of services
var hostServices = inputHost.configManager.serviceSystem.serviceInfo.service
var sshService = null
// loop the services, find the SSH service.
for each(svc in hostServices) {
  if(svc.key == "TSM-SSH") {
    sshService = svc
    System.log("Found SSH Service on host ["+inputHost.name+"]")
    break
  }
}
if(sshService == null) {
  throw "Couldn't find SSH service on ["+inputHost.name+"]!"
}

// Enable the service
try {
  inputHost.configManager.serviceSystem.startService(sshService.key)
} catch(err) {
  System.log("ERROR--Couldn't start SSH service. Error Text: "+err)
}
// the end

So, once you have SSH started on your ESXi host, you can send commands through VCO to do what you need.

SSH Service Check Action

For a more robust workflow, you will probably want an Action that will check to see if the service is running, and return a boolean value. That way you can build in a little bit more into the flow.
The setup for the ‘check’ Action is the same, with the exception of the return value being a boolean.

Setting up the Check SSH Action.
Setting up the Check SSH Action.

The code is similar as well, just doing a simple check at the end.

var hostservices = inputHost.config.service.service
var sshSvc = null
for each(svc in hostservices) {
  // System.log("Service: "+svc.label+", Status is: "+svc.running)
  if(svc.label == "SSH") {
    sshSvc = svc
    break
  }
}

// check status, return true/false
if(sshSvc.running == true) {
  return true
} else {
  return false
}

Where’s my Burrit–VSA?

Before you power off the VSA VM you’ll want to make sure to vMotion your other guests to another node, or have a foolproof way of finding the VSA appliance on your host. Another Action to the rescue! Given an input ESXi host, this Action will query the VMs running on the host and check its tags out to see if it matches a specific value found on all VSA appliances. Note that these Tags are actually in the vCenter database, and not the Inventory Service Tags in the vSphere Web Client.

For purposes of this post, I’ll name the action getVSAonHost.

Setting up the VSA finder Action.
Setting up the VSA finder Action.
// for when you absolutely, positively need to make sure it's a VSA.
var vsaKey = "SYSTEM/COM.VMWARE.VIM.SVA"
// check the VMs on the host for the tag through a loop
for each(vm in inputHost.vm) {
  if(vm.tag) {
    for each(tag in vm.tag) {
      if(tag.key == vsaKey) {
        return vm
        break
      }
    }
  }
}

So now, you know you have the VM in question. You can then pass the VirtualMachine’s name property to your SSH command later.

Making a SSHandwich

With the ESXi host and the VSA VM in hand, you can execute the built in Run SSH Command workflow to do the final step.

Here’s the SSH command to send, which will find the VSA VM ID and power it off in one line, no questions asked:

VMID=$(vim-cmd vmsvc/getallvms | grep -i <VSA Name> | awk '{ print $1 }') && vim-cmd vmsvc/power.off $VMID

Begin by creating a new workflow, and create a single input parameter named inputHost, of type VC:HostSystem.
Then create three attributes in the General tab, naming them sshCommandhostSystemName and vmVSAName, all of type string.
Finally, create another attribute called vsaAppliance of type VC:VirtualMachine for use with the Action.

Next, drop your getVSAonHost Action into the schema, and bind the actionResult to vsaAppliance as seen below.

Binding Actions to the getVSAonHost Action.
Binding Actions to the getVSAonHost Action.

Next, drop a Scriptable Task into the Schema and bind inputHost and vsaAppliance on the IN tab. On the OUT tab, bind the attributes of hostSystemName and vmVSAName. We are effectively going to write a small blurb of code that hands off the name properties of the input objects to the output attributes for use later, along with creating the SSH command string.

Binding values to the Scriptable Task.
Binding values to the Scriptable Task.

In the Scripting Tab, we’ll use a few simple lines of code to perform the handoff of values.

// assign values to output attributes
hostSystemName = inputHost.name
vmVSAName = vsaAppliance.name
// create SSH command string using the input values
sshCommand = "VMID=$(vim-cmd vmsvc/getallvms | grep -i "+vmVSAName+" | awk \'{ print $1 }\') && vim-cmd vmsvc/power.off $VMID"

Finally, drop a copy of the Run SSH Command workflow into the schema. There are a lot of inputs here, so you will have to do some more complicated bindings. You can either force them to be input each time the workflow is run, set a static value, or bind to existing attributes.

Here’s what it looks like by default.

Setup for the SSH Workflow.
Setup for the SSH Workflow.

How you approach this part is largely up to you, but here is how I did it for this example.

The updated SSH Workflow Setup.
The updated SSH Workflow Setup.

You’ll notice for the username/password I set a static value for root and set passwordAuthentication to Yes, and changed the initial hostNameOrIP and cmd values to the attributes we created earlier. For the outputs, I created new local attributes to show the results.

Run the workflow and you should see that VSA go down!

As an aside, if HA is enabled in your VSA HA Cluster object, it will immediately attempt to restart the machine – so make sure you add in the capability to disable HA into your parent workflows first so that this doesn’t end up being a problem.

Sweet Reprieve

It’s a bit crazy the amount of effort it takes to work around a disabled method, but it does work. I don’t think it’s particularly great, and if you’re the only one who cares about the systems and don’t have audits, this may be enough.

But for my purposes this was just the first step of the journey. I did not end up actually using this way to do things, but figured it may be a good exercise to document the process.

The next post will take it in a different direction altogether, and a little bit closer to an API based solution that can also be audited.

Learning vCO – Beginner Actions, World 1-1

As David Hasslehoff would say, you need some action(s)!

In Orchestrator, Actions are akin to functions in typical programming languages – in this case, Javascript. Functions and Actions can run the spectrum in terms of complexity – some are extremely simple, others can be extremely complicated. But ultimately, if the Action is a repeatable task you’d rather call than write over and over again, making an Action is the sensible thing to do.

Out of the box, VMware piles on numerous Actions you can use in your workflows. These can range from typical things you want to pull from vCenter, such as getting a list of all VMs in a cluster, or getting all clusters in a datacenter. Others can get you a list of VMs that are running a particular OS, such as Windows or Linux.

This post will be the first in a series of creating basic Actions, moving to the more complex, along with integration into a workflow.

The best way to learn the value of Actions, much like anything in Orchestrator, is to work with them or create them regularly.

NOTE: If you are not familiar with JavaScript and wish to use Orchestrator, I would recommend getting familiar fast!
The best online and free JavaScript training I have seen is CodeAcademy. It can take up to 10 hours or so, but you work at your own pace, and get fantastic feedback as you go.

Time to get Modular

The first thing to do is to create your own module in Orchestrator. Creating a module is useful, as it allows you the ability to call your Actions from pretty much anywhere, and export/import them wherever you like. Plus, all the cool kids are sharing their Actions on FlowGrab, so get on it!

From the Design menu in the Orchestrator client, go to the Actions icon, which looks like a gear icon.

The vCO Actions Menu.
The vCO Actions Menu.

In here, you’ll see piles of Actions for many vSphere objects and other types, based on the plugins you have installed. Browse around and look at them – you’ll see many of them are not particularly complicated scripts, but it does vary. Our example in this post will be VERY simple, but it will give you a good sense of how to both build an Action, and call the Action, from top to bottom.

So, let’s create your own module. The typical scheme is a reverse FQDN format. In the screenshot above it is A.NSFV.LAB.REPOSITORY. I could have followed the domain, but I like my stuff at the top!

With that done, right-click the module you created and choose Add Action. Name the Action combineTwoParameters.

Creating your new Action.
Creating your new Action.

Once it is created, you will immediately be taken into the Action for editing. When creating an Action, it’s good to have an idea of what is going in and what you are expecting will come out of it when the code is done running.

Setting up your Action.
Setting up your Action.

In our case, we are doing a simple combination of two values we input into the parameter listing, so the setup and code should be very easy.

Click the 03-edit-action-arrowicon icon above the parameter listing twice. This will create two input parameters named arg0 and arg1, of type String.

Once you have created the parameters, click the Return Type option (which is void by default), and choose the type String. This is probably one of the most important things to do – if you do not specify the return type, the Action will always return nothing!

Adding parameters to your Action.
Adding parameters to your Action.

With that done, below the parameter list will be your code window, as seen here with the API Explorer on the left.

The Action code window.
The Action code window, and your API Explorer.

Now, you’re ready to code the Action itself.
Since we’re keeping it simple for this example, you can copy/paste the code below.

var output = arg0+arg1
System.log("The combined value: "+output)
return output

Admittedly, that’s pretty easy! We’re taking the arg0 and arg1 values and just combining them, assigning the value to the variable output.

Then, we are returning the value of the output variable.

Once this is done, click Save and Close, and go to the Workflows tab.
You’re done! But now it’s time to show you a couple of ways you can use your new Action.

Using your Action in the GUI

The first way to use the Action you created is to use the GUI and add it to a workflow.

Begin by right-clicking and choosing Add Folder. Create your own folder of choice, outside of the standard workflow library.

Next, right-click and choose New Workflow. I named mine based on the post, but obviously, you can name it to your choosing.

Long name for a simple workflow.
Long name for a simple workflow.

When you have created the workflow, it will take you straight into editing mode. Click on the Schema tab.

A workflow's blank slate.
A workflow’s blank slate.

In the search bar, you can type the name of your Action and it should show up. Click and drag it into the workflow area and place it on the arrow.

Adding your Action to the new workflow.
Adding your Action to the new workflow.

At this point, you’ll see a message pop up about adding the “activity’s” input/output to the workflow. In this case, click Setup to do so.

Setting up Action and Workflow bindings.
Setting up Action and Workflow bindings.

This menu can help get the values of your action assigned to the workflow more efficiently, or mapped/bound to other attributes.  For this exercise, arg0 and arg1 can be set to Input, and the actionResult can be set to Local Variable. Then, click Promote.

Now, you can run the workflow, and give it a go.

This should be pretty easy.
This should be pretty easy.
Run that bad boy!
Run that bad boy!

The Trace View should output the value of the two strings added together.

The moment of truth!
The moment of truth!

In addition, the value has been assigned to the workflow attribute actionResult, and you could bind that attribute to other elements in the workflow as an argument or parameter.

Using your Action, in a script

I indicated earlier that Actions can be called in different ways.  Here’s an example of doing just that, in a single Scriptable Task element.

Instead of dropping a copy of the Action you created into the workflow, drop in a Scriptable Task.

Using a scriptable task instead of your Action.
Using a scriptable task instead of your Action.

These elements are generally used for specific tasks that are unique to the workflow.
They can also be the only thing a workflow designer uses if they are particularly comfortable and familiar with the APIs they are working with.

Once you have added the Scriptable Task element, edit it as-is. Input the following lines of code:

var output = System.getModule("a.nsfv.lab.repository").combineTwoParameters("Jump To","Conclusions Mat")
System.log(output)

This format should be fairly simple to follow, but let’s break it down.
Remember that module you made at the beginning of the post?  This is where it comes into play.

You are defining a variable, output, and calling the Action combineTwoParameters which resides inside the module a.nsfv.lab.repository. When you call it, you are assigning the same string values you did earlier to the arguments in parentheses.

(Hopefully, that made some sense. I’m still working on my delivery.)

An interesting side note: if you do drag and drop the Action into your workflow and attempt to edit it, click on the Scripting tab. In there, you will see a line of code which looks just like the above. So the Action you are dragging into your workflow schema is really just an automatically generated script that calls an instance of your Action code.

How to call your Action code programatically, exposed!
How to call your Action code programatically, exposed!

I promise it’s not as complicated as it sounds. If anything, it is reducing the amount of code you would otherwise write.

Theoretically, you could call countless Actions in a single Scriptable Task. But you may not necessarily want to. The decision is up to you, but my advice would be to do as much as you can in the GUI so that others can more easily follow your work if they need to visit it. Not only that, I find it helps me out too, when I’m working on complicated workflows. It’s all a matter of preference.

My next post will continue with some simple VM tasks and working with the API Explorer. In the meantime, practice up, and look at the Actions that are already available to you. You may find that there are quite a few gaps – hopefully after going through these posts, you’ll be able to fill in your own gaps!