How to use the REST API to Start a Workflow

It seems everyday the interest in vCenter Orchestrator (vCO) is growing. I love this because it brings more and more use cases to our attention. One such use case is the need to call vCO workflows via its REST API. In order to keep this article generic, but useful, I will work with the free RESTClient plug-in/add-on for Firefox as the client. The general process here will be similar to what you may need to do from your software or portal to integrate with vCO’s REST API.

Tutorial Goal

RESTClient-vCO-LocalProcess_2685ed0d4162e5e0887bc76a4b71d24d.png

So, with that in mind, the goal of this tutorial is to learn how to execute the “Hello Perl” workflow from this tutorial: How to run a Perl Script from a vCenter Orchestrator Workflow via the REST API.

In order to execute a workflow via the REST API, you must:

  • Identify the format you wish to work in: REST/JSON
  • Locate the Workflow ID you want to run
  • Understand the proper body format to submit the workflow inputs in
  • Understand how to check the status/results of the workflow

We’ll step through these various concepts, providing step-by-step instructions on what needs to be done for each.

Concepts Covered In this tutorial

The following concepts will be covered:

  • Accessing the REST API and Documentation
  • Setting headers to force JSON vs. XML
  • How to filter GET results
  • Finding a Workflow ID
  • How to get proper XML/JSON format for input parameters via REST
  • Executing a Workflow
  • Checking Workflow results

Tools Used

The following tools are used for this tutorial:

  • vCenter Orchestrator 5.5 Appliance (Important to use the 5.1.1 or newer version as there is a json issue in prior 5.x releases)
  • Mozilla Firefox with the RESTClient installed
  • Simple PERL Script from previous article

Accessing the vCO REST API and Documentation

Accessing_the_vCO_REST_API_and_Documentation.png

If you have a vCO 5.x server and newer (Windows or appliance) running then you have the REST API and its documentation readily available.

5.0 -> 5.1.x : The root url for the API is: https://:8281/api/ 5.5 and newer : The root url for the API is: https://:8281/vco/api/

The root page for the documentation is found on your vCO server at https://:8281/vco/api/docs

Of particular interest for this article are the “Workflow Service” and “Workflow Presentation Service” links shown on that page. Those two pages provide considerable detail on how to get and execute workflows via REST. Take a moment to explore those two pages before continuing with this tutorial as I will not be re-documenting those pages here.

The goal of the process detailed in the next several steps is to identify the correct URL for the workflow we wish to run… but in order to do that, we must first locate the workflow in the api. This whole process is only needed when you are discovering the ID of a workflow for the first time. Once identified, your only concern will be around getting the inputs correct for a successful launch of the workflow.

Setting headers to force JSON vs. XML

The vCO REST API defaults to using XML. If you are more comfortable with, or your system uses, JSON then you can provide a header that tells vCO to expect and reply using JSON instead of XML. Let’s do that now with Firefox and the RESTClient plug-in (feel free to use the REST client of your choice - for the purposes of this article, the RESTClient will be used.)

Set Custom Header in RESTClient

Set_Custom_Header_in_RESTClient.png

Click “Headers”, then click Custom Header

Accept: application/json

Accept_applicationjson.png

As shown in the screenshot above, set an “Accept” header with a value of “application/json” and click Okay for vCO 5.5. Now, whenever responses come back from the API they will be in JSON format.

**NOTE: If you are using vCO 5.1.1 there is a slight bug in the header. In order to get json responses, you must set the “Accept” header to a value of “application/json;v=5.1.1"

How to filter GET results

When accessing the /vco/api/workflows url of the REST API, the API will return XML (or JSON) results of ALL workflows on the server. Depending on the number of workflows (Several hundred on a clean install of vCO), it could take a while to get the results and perhaps longer to locate the workflow you wish to run. If you really want to see those results, go ahead and submit a GET to your vCO server - https://:8281/vco/api/workflows

In order to filter the results and likely find your exact workflow, adjust your GET url to include /?condition=name=

Note: If the workflow name has spaces, replace the spaces with %20

In this tutorial, we need to get the “Hello PERL” workflow so our api url will be: https://:8281/vco/api/workflows/?conditions=name=hello%20perl

Note: The name of the workflow in the query is NOT case sensitive

Once you have run the workflow in the format of your choice (JSON/XML), you will need to identify the “id” of the workflow so that you can get the correct url to view the workflow and eventually run it. The best property in the results will actually be the “itemHref” as it provides the full URL (id included) to view the workflow. In my case, my id is 885edab8-4b31-466c-bfd4-9e2cd6db3b95 and the itemHref is https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/.

JSON Get Workflow Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
  "link": [
    {
      "attributes": [
        {
          "value": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/",
          "name": "itemHref"
        },
        {
          "value": "885edab8-4b31-466c-bfd4-9e2cd6db3b95",
          "name": "id"
        },
        {
          "value": "Hello Perl July 2013",
          "name": "categoryName"
        },
        {
          "value": "true",
          "name": "canExecute"
        },
        {
          "value": "https://vco55.vcoteam.lab:8281/vco/api/catalog/System/WorkflowCategory/ff808081417e9cd901417eb52141158a/",
          "name": "categoryHref"
        },
        {
          "value": "This workflow provides an example of using the command object to run a PERL script that resides on the local vCO filesystem. ",
          "name": "description"
        },
        {
          "value": "Hello PERL",
          "name": "name"
        },
        {
          "value": "false",
          "name": "customIcon"
        },
        {
          "value": "Workflow",
          "name": "type"
        },
        {
          "value": "true",
          "name": "canEdit"
        },
        {
          "value": "0.0.1",
          "name": "version"
        }
      ],
      "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/",
      "rel": "down"
    }
  ],
  "total": 1
}

XML Get Workflow Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<inventory-items xmlns="http://www.vmware.com/vco" total="1">
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/" rel="down">
        <attributes>
            <attribute value="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/" name="itemHref"/>
            <attribute value="885edab8-4b31-466c-bfd4-9e2cd6db3b95" name="id"/>
            <attribute value="Hello Perl July 2013" name="categoryName"/>
            <attribute value="true" name="canExecute"/>
            <attribute value="https://vco55.vcoteam.lab:8281/vco/api/catalog/System/WorkflowCategory/ff808081417e9cd901417eb52141158a/" name="categoryHref"/>
            <attribute value="This workflow provides an example of using the command object to run a PERL script that resides on the local vCO filesystem.&amp;#xA;" name="description"/>
            <attribute value="Hello PERL" name="name"/>
            <attribute value="false" name="customIcon"/>
            <attribute value="Workflow" name="type"/>
            <attribute value="true" name="canEdit"/>
            <attribute value="0.0.1" name="version"/>
        </attributes>
    </link>
</inventory-items>

Preparing to Run a Workflow

Once you have identified the execution URL, you are ready to prepare the Body of your REST message. As noted in the API Documentation for the Workflow Run Service, the endpoint url to POST to for running a workflow is: https://host:port/vco/api/workflows/{workflowId}/executions/

Additionally, pay close attention to the XML sample provided for the Body - it differs from what is received from the GET request we received earlier. In particular, your body must follow this format:

1
2
3
4
5
<execution-context xmlns="http://www.vmware.com/vco">
    <parameters>
        <!-- Your parameter entries here even if workflow does not require inputs, your REST POST requires a body! -->
    </parameters>
</execution-context>

So that means, for my workflow that would be:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<execution-context xmlns="http://www.vmware.com/vco">
    <parameters>
        <parameter type="string" name="scriptName" scope="local">
            <string>/orchestrator/helloperl.pl</string>
        </parameter>
        <parameter type="string" name="name" scope="local">
            <string>Burke</string>
        </parameter>
    </parameters>
</execution-context>

Depending on the object type of the inputs, the value will appear a little differently. You can see an example of this in the Workflow Run Service API documentation.

The above code prepares your inputs parameters if you’re using XML - but what about JSON? Ah, that is very simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "parameters": [
    {
      "value": {
        "string": {
          "value": "/orchestrator/helloperl.pl"
        }
      },
      "type": "string",
      "name": "scriptName",
      "scope": "local"
    },
    {
      "value": {
        "string": {
          "value": "Burke"
        }
      },
      "type": "string",
      "name": "name",
      "scope": "local"
    }
  ]
}

NOTE: If you workflow does not require any inputs, then you must still provide a body of empty curly-braces: {}

That’s nice, but HOW did you figure that out?

Oh, right - you’ll need to figure out what YOUR inputs should look like. In order to do that most effectively:

  1. Run your workflow once using the vCO client - this way, you can put in all your inputs and have a workflow execution available with those inputs stored
  2. Now, do a GET request to your workflow executions: https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/
  3. You should have one or more "
  4. Perform a GET request to one of your executions to see what the inputs look like

Here is the resulting XML and JSON results for the execution I noted above:

XML

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workflow-execution xmlns="http://www.vmware.com/vco" href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/">
    <relations>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/" rel="up"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/" rel="remove"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/logs/" rel="logs"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/state/" rel="state"/>
    </relations>
    <id>ff80808141c183890141c1e22f2a0012</id>
    <state>completed</state>
    <input-parameters>
        <parameter type="string" name="scriptName" scope="local">
            <string>/orchestrator/helloperl.pl</string>
        </parameter>
        <parameter type="string" name="scriptParams" scope="local">
            <string>Burke</string>
        </parameter>
    </input-parameters>
    <output-parameters>
        <parameter type="string" name="scriptOutput" scope="local">
            <string>Hello, Burke</string>
        </parameter>
        <parameter type="number" name="scriptResult" scope="local">
            <number>0.0</number>
        </parameter>
    </output-parameters>
    <start-date>2013-10-16T11:28:47.914-04:00</start-date>
    <end-date>2013-10-16T11:28:49.038-04:00</end-date>
    <started-by>bazbill</started-by>
    <name>Hello PERL</name>
</workflow-execution>

JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
  "relations": {
    "link": [
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/",
        "rel": "up"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/",
        "rel": "remove"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/logs/",
        "rel": "logs"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/state/",
        "rel": "state"
      }
    ]
  },
  "id": "ff80808141c183890141c1e22f2a0012",
  "state": "completed",
  "name": "Hello PERL",
  "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c1e22f2a0012/",
  "input-parameters": [
    {
      "value": {
        "string": {
          "value": "/orchestrator/helloperl.pl"
        }
      },
      "type": "string",
      "name": "scriptName",
      "scope": "local"
    },
    {
      "value": {
        "string": {
          "value": "Burke"
        }
      },
      "type": "string",
      "name": "scriptParams",
      "scope": "local"
    }
  ],
  "output-parameters": [
    {
      "value": {
        "string": {
          "value": "Hello, Burke"
        }
      },
      "type": "string",
      "name": "scriptOutput",
      "scope": "local"
    },
    {
      "value": {
        "number": {
          "value": 0
        }
      },
      "type": "number",
      "name": "scriptResult",
      "scope": "local"
    }
  ],
  "start-date": 1381937327914,
  "end-date": 1381937329038,
  "started-by": "bazbill"
}

The important code in the above code is in the “input-parameters” section of each example. You can see there the format of each of the input parameters.

NOTE: When building the body of your POST, be sure to use “parameters”, NOT “input-parameters”. Also, when using XML - be sure to use the “execution-context” tag as shown here and in the API documentation.

You now have:

  • Header for receiving back JSON content
  • Execution URL (endpoint address to POST)
  • Format of body in XML or JSON

The final piece, IF using JSON, is to tell the vCO API that you are sending JSON content in your POST. This is pretty simple, just add another custom header:

Content-Type: application/json

Ready to Run! - JSON

Ready_to_Run__-_JSON.png

This screenshot shows what my RESTClient looks like when all of this has been brought together to run the workflow using JSON.

Required HEADERs to post using JSON: Accept: application/json Content-Type: application/json

Ready to Run! - XML

Ready_to_Run__-_XML.png

This screenshot shows what my RESTClient looks like when all of this has been brought together to run the workflow using XML.

Required HEADERs to post using XML: Content-Type: application/xml

NOTE: Accept is not required since the default response type is already XML.

Expected Results From POST

Expected_Results_From_POST.png

Regardless of which format you use for the body of your POST, the expected result is an EMPTY BODY and a set of response headers as shown in the image above.

The Status Code of 202 means that you successfully started a workflow - NOT that the workflow was successful. There is a big difference there. In order to determine whether or not a workflow run was successful, you need to perfrom a GET request on the URL in the Location header + “state” … so, for example, To see whether or not my run was successful, I would perform my GET against this url:

https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c5110d50002d/state

Possible State Values

  • waiting
  • failed
  • completed
  • canceled
  • running

Successful JSON State Example

Successful_JSON_State_Example.png

And for the Body:

{"value":"completed"}

Successful XML State Example

Successful_XML_State_Example.png

And for the Body:

1
2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<state xmlns="http://www.vmware.com/vco">completed</state>

Failure Example

The Response Headers for the state url will be the same for Success and failure - the difference will be in the body. A failed body looks like this:

JSON

{"value":"failed"}

XML

1
2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<state xmlns="http://www.vmware.com/vco">failed</state>

Failure Details

In the event of a failure, more details can be obtained by performing a GET request against the workflow execution. Earlier, I had you just do the get request against the workflow executiion url + /state since there is less parsing involved. Here’s what a workflow execution looks like:

JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
  "relations": {
    "link": [
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/",
        "rel": "up"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/",
        "rel": "remove"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/logs/",
        "rel": "logs"
      },
      {
        "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/state/",
        "rel": "state"
      }
    ]
  },
  "id": "ff80808141c183890141c53582eb0039",
  "state": "failed",
  "name": "Hello PERL",
  "href": "https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/",
  "input-parameters": [
    {
      "value": {
        "string": {
          "value": "/orchestrator/helloperl.pl"
        }
      },
      "type": "string",
      "name": "scriptName",
      "scope": "local"
    },
    {
      "value": {
        "string": {
          "value": "Burke"
        }
      },
      "type": "string",
      "name": "scriptParams",
      "scope": "local"
    }
  ],
  "output-parameters": [
    {
      "type": "string",
      "name": "scriptOutput",
      "scope": "local"
    },
    {
      "type": "number",
      "name": "scriptResult",
      "scope": "local"
    }
  ],
  "start-date": 1381993120491,
  "end-date": 1381993121574,
  "started-by": "bazbill",
  "content-exception": "Example failure - something went horribly wrong!!! (Workflow:Hello PERL / Run Script (item1)#11)",
  "current-item-display-name": "Run Script"
}

XML

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workflow-execution xmlns="http://www.vmware.com/vco" href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/">
    <relations>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/" rel="up"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/" rel="remove"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/logs/" rel="logs"/>
        <link href="https://vco55.vcoteam.lab:8281/vco/api/workflows/885edab8-4b31-466c-bfd4-9e2cd6db3b95/executions/ff80808141c183890141c53582eb0039/state/" rel="state"/>
    </relations>
    <id>ff80808141c183890141c53582eb0039</id>
    <state>failed</state>
    <input-parameters>
        <parameter type="string" name="scriptName" scope="local">
            <string>/orchestrator/helloperl.pl</string>
        </parameter>
        <parameter type="string" name="scriptParams" scope="local">
            <string>Burke</string>
        </parameter>
    </input-parameters>
    <output-parameters>
        <parameter type="string" name="scriptOutput" scope="local"/>
        <parameter type="number" name="scriptResult" scope="local"/>
    </output-parameters>
    <start-date>2013-10-17T02:58:40.491-04:00</start-date>
    <end-date>2013-10-17T02:58:41.574-04:00</end-date>
    <started-by>bazbill</started-by>
    <name>Hello PERL</name>
    <content-exception>Example failure - something went horribly wrong!!! (Workflow:Hello PERL / Run Script (item1)#11)</content-exception>
    <current-item-display-name>Run Script</current-item-display-name>
</workflow-execution>

As you can see, there is quite a bit more content here.. In any case, to get the exception details, retrieve the value of the “content-exception”.

Conclusion

Throughout this tutorial, you have been provided details on:

  • Locating a specific workflow’s ID using a filtered GET query against the REST API
  • Identifying the correct Input parameters and the proper formatting to use (by looking at a past execution)
  • Executing a workflow
  • Retrieving workflow results
  • Working with XML or JSON

You should now be able to execute workflows via the REST api.

How About SecureString Inputs

This topic was added on May 18th, 2015 after having numerous requests on the topic…. Here’s a JSON example of passing a SecureString input type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{"parameters":[{
    "name":"secInput",
    "type":"SecureString",
    "scope":"local",
    "value":{
        "string":{"value":"test"}
    }
}]}

And an XML example:

```xml
<execution-context xmlns="http://www.vmware.com/vco">
    <parameters>
        <parameter type="SecureString" name="secInput" scope="local">
            <string>My Secure String Here</string>
        </parameter>
    </parameters>
</execution-context>

Note that the parameter type is SecureString, but the actual value is designated as a string.

Next Steps

This article walked through the basics of getting a simple STRING Input based workflow to run. The next step would be to figure out how to get and provide complex objects as inputs to workflows via the REST API. Different approaches may be used here:

  1. Use the REST API Inventory: https://[YOURVCOSERVER]:8281/vco/api/inventory – then drill down into the appropriate plug-in to find the object you need
  2. Create a vCO workflow that you can run easily (string inputs or no inputs for example) that returns an array of the objects you need

When dealing with any input parameters, remember to execute a workflow first using the vCO client and then view that execution as described in the article above to see how the REST API formats the inputs for various object types. Note, however, that the value for SecureString inputs will not be displayed here as the values are not stored in the workflow token.

Good Luck!