Aria Automation Custom Resource Types - The Easy Way
How to define custom resource types in vRA8 easily
Table of Contents
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