Creating a snapshot via the vRA API

Sunday, January 27, 2019

In this post I describe the 6 steps needed to create a snapshot of a VM via the vRealize Automation API. It took me a while to figure out how to verify the state of the request. 

The short version is that the “state” can be checked via the URL that is send back in the POST response “Location” header. Checking the URL right after posting the request shows an “IN-PROGRES” state, the same result as in the vRA portal. After repeatedly calling the URL the status changes into “SUCCESFULL” or “FAIL”. 

The headers from the POST response:

HTTP/1.1 201
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
ETag: "0"
Location: https://vpc.tisgoud.nl/catalog-service/api/consumer/requests/9e98ebfc-8736-4980-a026-ade761255295
Content-Type: application/json;charset=UTF-8
Content-Length: 0
Date: Thu, 24 Jan 2019 07:34:32 GMT
Connection: close
X-Frame-Options: SAMEORIGIN

Response from calling the URL in the “Location” header:

{
  "@type": "ResourceActionRequest",
  "id": "9e98ebfc-8736-4980-a026-ade761255295",
  "iconId": "5fccca72-4b09-41b6-a216-da10f388e352_icon",
  "version": 5,
  "requestNumber": 740489,
  "state": "SUCCESSFUL",
  "description": "Called via API",
  "reasons": null,
  "requestedFor": "[tenantName]@vpc.tisgoud.nl",
  "requestedBy": "[tenantName]@vpc.tisgoud.nl",
  "organization": {
    "tenantRef": "[tenantName]",
    "tenantLabel": "[tenantName]",
    "subtenantRef": "50b0bc4b-b0f5-42a9-9288-e035ea8208ba",
    "subtenantLabel": "[tenantName]"
  },

In the vRA API documentation I could not find a way to check the state of a request. The use of the “Location” header with a URL for checking the request state was at least unexpected. I can now use this to automate the creation of a snapshot.

HTTP location - Wikipedia

Overview of the vRealize Automation REST API

The full 6 steps to get to creating a snapshot are described below. 

Step 1 - Get bearer token

The first step is to retrieve the API bearer token with the following request:

POST /identity/api/tokens HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest
Content-Length: 110

{
  "username": "[tenantName]@vpc.tisgoud.nl",
  "password": "[password]",
  "tenant": "[tenantName]"
}

Response:

HTTP/1.1 200
Cache-Control: no-cache, no-store
Pragma: no-cache
Expires: Wed, 31 Dec 1969 23:59:59 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 400
Date: Thu, 24 Jan 2019 07:32:50 GMT
Connection: close
X-Frame-Options: SAMEORIGIN

{"expires":"2019-01-24T15:32:51.000Z","id":"MTU0OCDxNTE4MTEyMvpiNzhjMDA…","tenant":"[tenantName]"}

Step 2 - Get resources

In step 2 we request an inventory of the tenant resources. To speed up the response we use the URL params “page” and “limit”.

The URL has the following format: /catalog-service/api/consumer/resources

The request:

GET /catalog-service/api/consumer/resources?page=1&limit=5000 HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Bearer MTU0OCDxNTE4MTEyMvpiNzhjMDA…
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest

Partial response:

{
      "@type": "CatalogResource",
      "id": "2a415ba9-81f5-4bff-b35f-bccfd5587165",
      "iconId": "Infrastructure.CatalogItem.Machine.Virtual.vSphere",
      "resourceTypeRef": {
        "id": "Infrastructure.Virtual",
        "label": "Virtual Machine"
      },
      "name": "[tenantPrefix][VMname]",
      "description": null,
      "status": "ACTIVE",
      "catalogItem": null,
      "requestId": "96777ad8-a62b-48a9-bbfc-8969e3867db4",
      "providerBinding": {
        "bindingId": "93f1fb32-b510-4a15-bd37-dbd7696796e6",
        "providerRef": {
          "id": "f6c45002-e1e0-4842-9a2f-2a17c8bb09bd",
          "label": "Infrastructure Service"
        }
      },

In the response we search for the [VMname], for which we want to perform the snapshot. A few lines above the [VMname], in the “id” field we can find the “resourceId” which we need for the following steps.

Step 3 - Get resource actions

With the “resourceId” we can retrieve a list of actions we can perform on the resource. 

The URL has the following format: /catalog-service/api/consumer/resources/[resourceId]/actions

The request:

GET /catalog-service/api/consumer/resources/2a415ba9-81f5-4bff-b35f-bccfd5587165/actions/ HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Bearer MTU0OCDxNTE4MTEyMvpiNzhjMDA…
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest

Partial response:

{
  "links": [],
  "content": [
    
    {
      "@type": "ConsumerResourceOperation",
      "name": "Create VM Snapshot",
      "description": "Create a snapshot of a virtual machine.",
      "iconId": "5fccca72-4b09-41b6-a216-da10f388e352_icon",
      "type": "ACTION",
      "id": "fcf490d5-a7e9-4640-be83-ac74d4484c91",
      "extensionId": null,
      "providerTypeId": "com.vmware.csp.core.designer.service",
      "bindingId": "[tenantName]!::!187da47b-fdb6-44e5-9ceb-0b83e9a035ff",
      "hasForm": true,
      "formScale": "BIG"
    },
    
  ]
}

In the response we find the “actionId” in the “id” field needed for the next step.

Step 4 - Get resource action template

Templates can be different for each action request. To retrieve the template for the snapshot request we use the “resourceId and the “actionId”

The URL has the following format: /catalog-service/api/consumer/resources/[resourceId]/actions/[actionId]

The request:

GET /catalog-service/api/consumer/resources/2a415ba9-81f5-4bff-b35f-bccfd5587165/actions/fcf490d5-a7e9-4640-be83-ac74d4484c91/requests/template HTTP/1.1
Content-Type: application/json
Accept: application/json
Authorization: Bearer MTU0OCDxNTE4MTEyMvpiNzhjMDA…
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest

Response:

{
  "type": "com.vmware.vcac.catalog.domain.request.CatalogResourceRequest",
  "resourceId": "2a415ba9-81f5-4bff-b35f-bccfd5587165",
  "actionId": "fcf490d5-a7e9-4640-be83-ac74d4484c91",
  "description": null,
  "data": {
    "provider-__ASD_PRESENTATION_INSTANCE": null,
    "provider-__asd_tenantRef": "[tenantName]",
    "provider-deleteExisting": null,
    "provider-description": null,
    "provider-existingSnapshotName": null,
    "provider-name": null
  }
}

The response displays the template to request the snapshot.

Step 5 - Post resource action

Now that we have the template we can post the request. The template from the previous step is used as base for the body of the request. Although the template contained the “resourceId” and the “actionId” it was not needed in the actual request.

The URL has the following format: /catalog-service/api/consumer/resources/[resourceId]/actions/[actionId]/requests

The request:

POST /catalog-service/api/consumer/resources/2a415ba9-81f5-4bff-b35f-bccfd5587165/actions/fcf490d5-a7e9-4640-be83-ac74d4484c91/requests/ HTTP/1.1
Content-Type: application/json;charset=UTF-8
Accept: application/json;charset=UTF-8
Authorization: Bearer MTU0OCDxNTE4MTEyMvpiNzhjMDA…
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest
Content-Length: 451

{
  "type": "com.vmware.vcac.catalog.domain.request.CatalogResourceRequest",
  "description": "Called via API",
  "data": {
    "provider-__ASD_PRESENTATION_INSTANCE": null,
    "provider-__asd_tenantRef": "[tenantName]",
    "provider-deleteExisting": true,
    "provider-description": "Called via API (Paw) 27-01-2019, 15:51:18",
    "provider-existingSnapshotName": null,
    "provider-name": "Snapshot created via API 27-01-2019, 15:51:18"
  }
}

Response:

HTTP/1.1 201
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
ETag: "0"
Location: https://vpc.tisgoud.nl/catalog-service/api/consumer/requests/9e98ebfc-8736-4980-a026-ade761255295
Content-Type: application/json;charset=UTF-8
Content-Length: 0
Date: Thu, 24 Jan 2019 07:34:32 GMT
Connection: close
X-Frame-Options: SAMEORIGIN

The “Location” field in the response header contains the URL we need for checking the status of the request.

Step 6 - Get request result state

With the URL from “Location” header we retrieve the status of the request. 

GET /catalog-service/api/consumer/requests/9e98ebfc-8736-4980-a026-ade761255295 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Accept: application/json;charset=UTF-8
Authorization: Bearer MTU0OCDxNTE4MTEyMvpiNzhjMDA…
Host: vpc.tisgoud.nl
Connection: close
User-Agent: Paw/3.1.8 (Macintosh; OS X/10.14.2) GCDHTTPRequest

Response:

{
  "@type": "ResourceActionRequest",
  "id": "9e98ebfc-8736-4980-a026-ade761255295",
  "iconId": "5fccca72-4b09-41b6-a216-da10f388e352_icon",
  "version": 5,
  "requestNumber": 740478,
  "state": "SUCCESSFUL",
  "description": "Called via API",
  "reasons": null,
  "requestedFor": "[tenantName]@vpc.tisgoud.nl",
  "requestedBy": "[tenantName]@vpc.tisgoud.nl",
  "organization": {
    "tenantRef": "[tenantName]",
    "tenantLabel": "[tenantName]",
    "subtenantRef": "50b0bc4b-b0f5-42a9-9288-e035ea8208ba",
    "subtenantLabel": "[tenantName]"
  },
  "requestorEntitlementId": "91861684-fdf3-4e05-bd68-64edd0e173a4",
  "preApprovalId": null,
  "postApprovalId": null,
  "dateCreated": "2019-01-24T07:34:32.612Z",
  "lastUpdated": "2019-01-24T07:35:12.920Z",
  "dateSubmitted": "2019-01-24T07:34:32.612Z",
  "dateApproved": null,
  "dateCompleted": "2019-01-24T07:35:12.920Z",
  "quote": {},
  "requestCompletion": {
    "requestCompletionState": "SUCCESSFUL",
    "completionDetails": "The request was successfully completed",
    "resourceBindingIds": null
  },
  "requestData": {
    "entries": [
      {
        "key": "provider-name",
        "value": {
          "type": "string",
          "value": "Snapshot created via API 24-01-2019, 08:34:32"
        }
      },
      {
        "key": "provider-deleteExisting",
        "value": {
          "type": "boolean",
          "value": true
        }
      },
      {
        "key": "provider-__asd_tenantRef",
        "value": {
          "type": "string",
          "value": "[tenantName]"
        }
      },
      {
        "key": "provider-existingSnapshotName"
      },
      {
        "key": "provider-__ASD_PRESENTATION_INSTANCE"
      },
      {
        "key": "provider-description",
        "value": {
          "type": "string",
          "value": "Called via API (Paw) 24-01-2019, 08:34:32"
        }
      }
    ]
  },
  "retriesRemaining": 3,
  "requestedItemName": "Create VM Snapshot - [tenantPrefix][VMname]",
  "requestedItemDescription": "Create a snapshot of a virtual machine.",
  "components": null,
  "stateName": "Successful",
  "phase": "SUCCESSFUL",
  "executionStatus": "STOPPED",
  "approvalStatus": "POST_APPROVED",
  "waitingStatus": "NOT_WAITING",
  "resourceRef": {
    "id": "2a415ba9-81f5-4bff-b35f-bccfd5587165",
    "label": "[tenantPrefix][VMname]"
  },
  "resourceActionRef": {
    "id": "fcf490d5-a7e9-4640-be83-ac74d4484c91",
    "label": "Create VM Snapshot"
  }
}

The status of the request is shown in the “state” field of the reponse. Making a snapshot of a VM takes some time which results in a “state” of “IN-PROGRESS”. After repeatedly requesting the state we get the final result. In the above response it is “SUCCESSFUL” when something goes wrong, the “state” is “FAIL” and the error is displayed in the response.