vRA8 XaaS Custom Destroy Workflow

Share on:

How to run a workflow at XaaS deployment deletion to free resources

Introduction

XaaS (Anything as a Service) is a great way to automate IT assets in vRA. But if your target objects are not natively supported by vRA/vRO, then making a create -> decommission lifecycle is not that easy. You can map a vRO object to a vRA custom resource, or use ABX with user defined schema to define a custom resource.

If you choose vRO object mapping, you either need to use existing vRO inventory objects (provided by plugins), or you need to create a new object type by using Dynamic Types or write a new plugin from scratch. None of them are easy.

If you choose ABX with schema definition, you need to write ABX actions (Python/NodeJS/PowerShell scripts) and you cannot modify the schema after creation.

Plain ol' vRO workflows

vRO workflows can be added to Service Broker (as a content source) and be run directly, resulting in a deployment. However, these deployments have no lifecycle operations, deleting simply removes the deployment from the list of resources provisioned (vRA7 called it Dismiss action). Our goal is to make vRA to call a destroy workflow that frees the resources created/reserved by the deploy workflow.

This will result in an easier to maintain solution: no need to create custom resources and we can reuse existing vRO workflows to mimic service decommission.

Our demonstation XaaS: a simple IPAM

The demo IPAM solution can do two things: reserve IP address(es) from a subnet, and free up a reserved IP address. No other information is stored so it is not production ready... The objects we'd like to manage are the addresses themselves. Two lifecycle operations are available: allocate and release.

The reserved addresses are stored in vRO resource elements (by subnet) so there are no external dependencies to setup and test this XaaS:

(In the example above the following IPs are reserved: 10.0.1.1, 10.0.1.4, 10.0.1.5)

Three workflows provide the functions:

  • Define new subnet
    creates a new vRO resource element to store IP addresses
  • IP Allocation
    allocates one or more addresses from the chosen subnet, registers in the resource element and returns the reserved address(es)
  • IP Release
    removes an address from the reserved list (resource element)

First import com.xaas.destroy.package from https://github.com/kuklis/vro8-packages into vRO, and create a content source in Service Broker:

Share the content source to a project:

Catalog items:

Start with defining your subnets (creating the empty resource elements):

New IP request:

Result of requesting 2 IPs:

The catalog item IP release is very simple with only one input: the address to remove from the list of allocated IPs.

vRA API

vRealize Automation REST API provides access to deployment data via the Deployment API:

1curl -s -k -H 'Content-Type: application/json' -H "Authorization: Bearer $access_token" \
2"https://vra8.corp.local/deployment/api/deployments/7de98f11-a490-4dc1-8f6f-da20ff638fce?expand=resources" | jq .

The response contains workflow inputs and outputs:

 1{
 2  "id": "7de98f11-a490-4dc1-8f6f-da20ff638fce",
 3  "name": "IP for NAS",
 4  "orgId": "673be853-8004-4216-95ad-3cb32e6754ab",
 5  "catalogItemId": "ea45a47f-ce9b-34e5-84d0-4b11738d9f4d",
 6  "blueprintId": "inline-blueprint",
 7  "createdAt": "2022-05-09T14:14:48.910894Z",
 8  "createdBy": "kuklis",
 9  "ownedBy": "kuklis",
10  "lastUpdatedAt": "2022-05-09T14:15:14.472976Z",
11  "lastUpdatedBy": "kuklis",
12  "inputs": {
13    "count": 2,
14    "subnet": "10.0.1"
15  },
16  "projectId": "33e98713-0902-4024-b023-6c4ffd802f53",
17  "resources": [
18    {
19      "id": "11d054d3-f70c-4d4e-abad-259d7d872618",
20      "name": "workflow",
21      "type": "vro.workflow",
22      "properties": {
23        "outputs": {
24          "IPs": {
25            "type": "Array/string",
26            "value": [
27              "10.0.1.2",
28              "10.0.1.3"
29            ]
30          },
31          "__metadata_orgId": {
32            "type": "string",
33            "value": "673be853-8004-4216-95ad-3cb32e6754ab"
34          },
35          "__metadata_userName": {
36            "type": "string",
37            "value": "kuklis"
38          },
39          "__metadata_projectId": {
40            "type": "string",
41            "value": "33e98713-0902-4024-b023-6c4ffd802f53"
42          },
43          "__metadata_requestId": {
44            "type": "string",
45            "value": "26c22be8-b4eb-4cb9-bf2a-338e88b6c28e"
46          },
47          "__metadata_deploymentId": {
48            "type": "string",
49            "value": "7de98f11-a490-4dc1-8f6f-da20ff638fce"
50          }
51        },
52        "resourceName": "IP Allocation"
53      },
54      "createdAt": "2022-05-09T14:15:00.276046Z",
55      "state": "OK"
56    }
57  ],
58  "status": "CREATE_SUCCESSFUL"
59}

Access Deployment API from vRO

For vRA REST API we use the Orchestrator Plug-in for vRealize Automation.

We create a service user apiuser that is able to read any deployment.
First we define a new role:

Then assign the service user (it was granted Organization Member and Service Broker User roles previously) to this role providing the extra permission:

and create a vRA endpoint within vRO:

Now apiuser can read any deployment via API. Here is how we extract the IP addresses from the workflow output:

 1var vraHost = VraHostManager.findHostsByType("vra-onprem").filter(
 2    function (host) {
 3        return host.name == 'apiuser'
 4    }
 5)[0];
 6
 7var restClient = vraHost.createRestClient();
 8var request = restClient.createRequest("GET", "/deployment/api/deployments/" +
 9  inputProperties.deploymentId + "?expand=resources");
10
11var response = restClient.execute(request);
12System.log("HTTP response code: " + response.statusCode);
13var content = JSON.parse(response.contentAsString);
14System.log("content = " + JSON.stringify(content, null, 2));
15
16addresses = content.resources[0].properties.outputs ? 
17  content.resources[0].properties.outputs.IPs.value : [];

XaaS destroy workflow

Now we create an EBS workflow to release all addresses found in the deployment output. Being an EBS all inputs are provided via the inputProperties input parameter. The above code (Get deployment info) collects the addresses and all we have to do is call the IP Release workflow to each address.

Event Broker Subscription

We want to run the destroy workflow only for IP Allocation deployments, so we identify the UUID of the catalog item:

Now define the EBS. The event topic is Deployment requested and we run the destroy workflow only if the request is DESTROY_DEPLOYMENT and the catalog item UUID identified matches:

The following logs are produced at deleting the IP Request deployment:

  12022-05-09 17:04:18.584 +02:00 INFO __item_stack:/item1
  22022-05-09 17:04:18.586 +02:00 INFO inputProperties = {
  3  "requestType": "CATALOG",
  4  "catalogItemVersion": "",
  5  "blueprintVersion": "",
  6  "description": "",
  7  "contextId": "51667ebf-7440-42b9-95f0-15428b7a78a7",
  8  "eventType": "DESTROY_DEPLOYMENT",
  9  "userName": "kuklis",
 10  "blueprintId": "inline-blueprint",
 11  "userId": "DEV.local:2c2bce76-279b-4aba-98c6-a55d00f5ee1b",
 12  "orgId": "673be853-8004-4216-95ad-3cb32e6754ab",
 13  "catalogItemId": "ea45a47f-ce9b-34e5-84d0-4b11738d9f4d",
 14  "deploymentId": "7de98f11-a490-4dc1-8f6f-da20ff638fce",
 15  "requestInputs": {},
 16  "id": "a0b70060-7aaf-422d-a2ff-54f179fa3e17",
 17  "projectName": "Prod1",
 18  "failureMessage": "",
 19  "projectId": "33e98713-0902-4024-b023-6c4ffd802f53",
 20  "status": ""
 21}
 222022-05-09 17:04:18.591 +02:00 INFO __item_stack:/item2
 232022-05-09 17:04:19.415 +02:00 INFO HTTP response code: 200
 242022-05-09 17:04:19.416 +02:00 INFO content = {
 25  "id": "7de98f11-a490-4dc1-8f6f-da20ff638fce",
 26  "name": "IP for NAS",
 27  "orgId": "673be853-8004-4216-95ad-3cb32e6754ab",
 28  "catalogItemId": "ea45a47f-ce9b-34e5-84d0-4b11738d9f4d",
 29  "blueprintId": "inline-blueprint",
 30  "createdAt": "2022-05-09T14:14:48.910894Z",
 31  "createdBy": "kuklis",
 32  "ownedBy": "kuklis",
 33  "lastUpdatedAt": "2022-05-09T15:04:16.691521Z",
 34  "lastUpdatedBy": "kuklis",
 35  "inputs": {
 36    "count": 2,
 37    "subnet": "10.0.1"
 38  },
 39  "projectId": "33e98713-0902-4024-b023-6c4ffd802f53",
 40  "resources": [
 41    {
 42      "id": "11d054d3-f70c-4d4e-abad-259d7d872618",
 43      "name": "workflow",
 44      "type": "vro.workflow",
 45      "properties": {
 46        "outputs": {
 47          "IPs": {
 48            "type": "Array/string",
 49            "value": [
 50              "10.0.1.2",
 51              "10.0.1.3"
 52            ]
 53          },
 54          "__metadata_orgId": {
 55            "type": "string",
 56            "value": "673be853-8004-4216-95ad-3cb32e6754ab"
 57          },
 58          "__metadata_userName": {
 59            "type": "string",
 60            "value": "kuklis"
 61          },
 62          "__metadata_projectId": {
 63            "type": "string",
 64            "value": "33e98713-0902-4024-b023-6c4ffd802f53"
 65          },
 66          "__metadata_requestId": {
 67            "type": "string",
 68            "value": "26c22be8-b4eb-4cb9-bf2a-338e88b6c28e"
 69          },
 70          "__metadata_deploymentId": {
 71            "type": "string",
 72            "value": "7de98f11-a490-4dc1-8f6f-da20ff638fce"
 73          }
 74        },
 75        "resourceName": "IP Allocation"
 76      },
 77      "createdAt": "2022-05-09T14:15:00.276046Z",
 78      "state": "OK"
 79    }
 80  ],
 81  "status": "DELETE_INPROGRESS"
 82}
 832022-05-09 17:04:19.422 +02:00 INFO __item_stack:/item3
 842022-05-09 17:04:19.435 +02:00 INFO __item_stack:/item3/item4
 852022-05-09 17:04:19.447 +02:00 INFO __item_stack:/item3/item2
 862022-05-09 17:04:19.464 +02:00 INFO __item_stack:/item3/item1
 872022-05-09 17:04:19.466 +02:00 INFO Allocated at the moment: 1,2,3,4,5
 882022-05-09 17:04:19.467 +02:00 INFO Releasing 10.0.1.2
 892022-05-09 17:04:19.473 +02:00 INFO __item_stack:/item3/item3
 902022-05-09 17:04:19.496 +02:00 INFO __item_stack:/item3/item5
 912022-05-09 17:04:19.509 +02:00 INFO __item_stack:/item3/item0
 922022-05-09 17:04:19.515 +02:00 INFO __item_stack:/item3
 932022-05-09 17:04:19.525 +02:00 INFO __item_stack:/item3/item4
 942022-05-09 17:04:19.539 +02:00 INFO __item_stack:/item3/item2
 952022-05-09 17:04:19.550 +02:00 INFO __item_stack:/item3/item1
 962022-05-09 17:04:19.552 +02:00 INFO Allocated at the moment: 1,3,4,5
 972022-05-09 17:04:19.553 +02:00 INFO Releasing 10.0.1.3
 982022-05-09 17:04:19.559 +02:00 INFO __item_stack:/item3/item3
 992022-05-09 17:04:19.578 +02:00 INFO __item_stack:/item3/item5
1002022-05-09 17:04:19.593 +02:00 INFO __item_stack:/item3/item0
1012022-05-09 17:04:19.598 +02:00 INFO __item_stack:/item3
1022022-05-09 17:04:19.603 +02:00 INFO __item_stack:/item0

At the end of the logs you can see releasing both allocated addresses.

Summary

Implementing your own destroy EBS is easy especially if you already have the vRO workflows to release the resources. It is a lightweight solution compared to Custom Resources.

Download the com.xaas.destroy.package containing the sample workflows from GitHub: https://github.com/kuklis/vro8-packages