Aria Automation Custom Resource Types - The Easy Way

Share on:

How to define custom resource types in vRA8 easily

Introduction

Creating Custom Resources based on Orchestrator Dynamic Types can be confusing for the first time. In this post I present a sample type to demonstrate a minimal functional implementation, that you can adapt to your usecase.

Aria Automation Custom Resources

In order to create our own representation of objects, vRA allows us to define Custom Resource Types. We can either use ABX actions or vRO to implement the CRUD operations. If we choose vRO, we can either reuse existing types (that are availabe in the Inventory), or define our own by using the Dynamic Types plugin.

This plugin has been part of Orchestrator for a long time, but it's documentation is very short. One can find couple of excellent blog posts on its usage. I found Martin Petkov's post excellent: not only he implements a real world complex usecase (EKS) in detail, but also explains some of the confusing details of Dynamic Types plugin.

Defining a new type - the usual approach

To follow most of the guides available we start with defining a Namespace (by running Library / Dynamic Types / Configuration / Define Namespace) and generating stub workflows (by running by running Library / Dynamic Types / Configuration / Define Type). We'll get the following workflows (for namespace myNS and type name myType1):

  • Find All myNS-myType1
  • Find myNS-myType1 By Id
  • Find Relation myNS-myType1
  • Has myNS-myType1 Children In Relation

We can create further types that behave like folders, subfolders or leaf nodes. By defining relations between them, we can build up a nice hierarchical, browsable object tree in vRO inventory:


Defining a new type - a simplified approach

To implement Aria Automation Custom Resource Type we do not have to create these folder(s) and relations. Let's see when are these workflows run and which one(s) can we omit writing.

WF Trigger
Find All myNS-myType1 This one is run in vRO if the input type is DynamicTypes:myNS.myType1 to seach and find objects based on input.
Also, when we run Server.findAllForType("DynamicTypes:myNS.myType1", query) to search for our objects, Find All is being called.
We can safely skip implementing this.
Find myNS-myType1 By Id We need this to create custom resource objects.
Find Relation myNS-myType1 Used for vRO inventory hierarchy, not needed.
Has myNS-myType1 Children In Relation Used for vRO inventory hierarchy, not needed.

So, in order to make our custom resource working, we only have to write the Find By Id workflow. By this we can save a lot of time and development effort (but we lose the ability to browse our objects in vRO inventory).

The demo type: myObject

With dynamic type objects (vRO) / custom resources (vRA) we represent exernal objects in Aria inventories. These resource instances cache the information of the "real" objects they represent.

For demonstration purposes we'll use database table records as objects to be represented by a dynamic type. It is easy to implement and test. For real world scenarios custom resources can model loadbalancer virtualhosts, virtual desktops, backup policies etc. Anything we want to manage and display. It is important to keep in mind that dynamic type objects are only mirroring information of external entitites (we do not need to store them in a database). In our demo case, these external objects are the database records.

The database table has the following shema:

  • id (nvarchar (50))
  • name (nvarchar (50))
  • weight (int)


Let's define a simple dynamic type called myObject with the following attributes:

  • id (mandatory)
  • name (mandatory)
  • lastrefreshed (custom attribute, timestamp)
  • weight (custom attribute, number)

DefineType myObject workflow:

1DynamicTypesManager.defineNamespace("myNS");
2DynamicTypesManager.defineType("myNS", "myObject", "/Library/Dynamic Types/images/item-16x16.png",
3    ["id", "name", "lastrefreshed", "weight"]);

The new type appears in vRO inventory:

Find By Id workflow

In order to get our new type working, we need to implement Find myNS-myObject By Id workflow. In our demo scenario this is a very simple SQL query. Real world cases typically involve (possibly) several API calls or command invocations.

Workflow inputs are type and id, output is the myObject instance found. The database is stored in the workflow variable db in our case.

 1System.log("requested type: " + type);
 2System.log("requested id: " + id);
 3
 4var records = [];
 5try {
 6  records = db.readCustomQuery("SELECT * FROM myObjects WHERE id='" + id + "'");
 7}
 8catch (e) {
 9    System.warn(e);
10}
11
12if (1 == records.length) {
13    var record = new Properties(); // convert ActiveRecord to HashMap
14    for each(var column in records[0].getFieldNames()) { record.put(column, records[0].getProperty(column)); }
15
16    myObject = DynamicTypesManager.makeObject("myNS", "myObject", id, record.name);
17    myObject.setProperty("lastrefreshed", new Date().toISOString());
18    myObject.setProperty("weight", record.weight);
19}
20else {
21    System.warn("Number of objects found with id '" + id + "': " + records.length);
22    myObject = DynamicTypesManager.makeObject("myNS", "myObject", id, "MISSING");
23    myObject.setProperty("lastrefreshed", "");
24    myObject.setProperty("weight", 0);
25}
26
27System.log("myObject:" + myObject.toSource());

We do an SQL SELECT by id, and set the properties of our dynamic type object based on the result. Best practice is to return a dummy object even if the query failed. This will come handy when we integrate our dynamic type as a custom resource in Cloud Assembly.

DynamicTypesManager.makeObject() creates the in-memory object that represents the external entity (a DB record, here). We set the the lastrefreshed property to the current timestamp to mark the creation time of the dynamic object. This is not necessary but useful when checking objects in Service Broker.

Binding workflow(s)

We need to bind the workflow(s) to the dynamic type. We implemented Find by Id only, so we bind only this workflow using its id (see Bind methods myObject workflow):

1DynamicTypesManager.bindTypeFinderMethods("myNS", "myObject", "c2b54c5e-e1c3-4cb8-8ec7-788dd7bd49c5");

vRO inventory view:

CRUD workflows

To make our dynamic type available in Aria Automation, we need to make the Create, Update and Destroy workflows.

Create myObject workflow reads inputs name and weight, generate a unique ID and insert a new record into the database. We call the Find by Id workflow by DynamicTypesManager.getObject() to return the dynamic type object as an output.

1var id = System.nextUUID();
2
3db.executeCustomQuery("INSERT INTO myObjects (id,name,weight) VALUES " +
4    "('" + id + "','" + name + "'," + Math.round(weight) + ")");
5
6myObject = DynamicTypesManager.getObject("myNS", "myObject", id); // call Find by Id
7
8System.log(myObject.toSource());

The Update myObject workflow will not do anything besides logging the object. We create it for future uses as it cannot be added/changed after the Custom Resource Type defined.

The Destroy myObject workflow will remove the relevant database record and return success.

1System.log("Destroying: " + input.toSource());
2db.executeCustomQuery("DELETE FROM myObjects WHERE id='" + input.id + "'");
3actionResult = true;

Cloud Assembly Custom Resource

In Cloud Assembly / Design / Custom Resources make a new type myObject:


The Properties Schema is automatically created based on the Create workflow inputs and the dynamic type properties:

 1type: object
 2properties:
 3  name:
 4    type: string
 5    title: name
 6  weight:
 7    type: number
 8    title: weight
 9  myObject:
10    type: object
11    properties:
12      id:
13        type: string
14        title: id
15      name:
16        type: string
17        title: name
18      type:
19        type: string
20        title: type
21      weight:
22        type: string
23        title: weight
24      namespace:
25        type: string
26        title: namespace
27      lastrefreshed:
28        type: string
29        title: lastrefreshed
30    computed: true
31required: []

Create a cloud template with the new custom resource type:

 1formatVersion: 1
 2inputs:
 3  name:
 4    type: string
 5  weight:
 6    type: integer
 7resources:
 8  myObject_1:
 9    type: Custom.myObject
10    properties:
11      name: ${input.name}
12      weight: ${input.weight}

The resulting deployment:

And the new record:

Refresh custom resource data

By default vRA runs Find by Id every 6 hours to refresh cached data:


To force refreshing data we can create a dummy day 2 action Refresh. This action does not have to do anything, we'll only log the inputs (Log myObject workflow):

1var myObject = System.getContext().getParameter("__metadata_resourceProperties").myObject;
2System.log("myObject: " + JSON.stringify(myObject));

Add it as an additonal action to the custom resource:

After using the day 2 action, we can see the lastrefreshed attribute is updated. We have updated the database manually, and weight property is reflecting the new value after re-reading the external object. Please note that the custom attribute defined by the input provided at creation time did not change.

To be continued

In the next part we'll examine how to change the dynamic type definition and the custom resource schema. You can find the sample workflows at GitHub: https://github.com/kuklis/vro8-packages