Add owners/descriptions to resources

Use Vanta's API to update the Owner and Descriptions on your Resources

Add Owners/Description to Resource Kinds

In this guide we'll walkthrough the various API Endpoints needed to assign Owners and Descriptions to your inventory resources on your Vanta instance.

In Vanta, your inventory resources are required to be assigned an active user as an Owner in order pass certain compliance tests such as the Inventory items have active owners test:

Similarly, Inventory resources are also required to have a text Description via the Inventory items have descriptions test:

This helps Auditors understand the purpose of the resource and show you're handling them responsibly.

In order to perform these actions through the Vanta API, we'll need to do the following.

Steps:

  • GET our list of connected integrations, or a known connected integration
    • (Optional) GET resource kind details for a connected integration
  • GET a list resources for that resource kind
    • (Optional) GET a list of active users to determine who should own the resources
  • PATCH a single resource, or multiple resources to set their owner and description


List Integrations Endpoint - List connected integrations

The integrations endpoint can be used to list all the integration ids, the resource kinds associated with that specific integration, as well as the integration's connectionIds. We'll also need the resourceKind and integrationId later on to filter what kind of resources we assign Owner and Descriptions for.

If you don't know the IntegrationId of the integration that the resources belong to, use this endpoint to return a list of connected integrations, along with their resource kinds.

Endpoint:

/integrations

Query Parameters:

  • pageSize (<number>): An integer value that specifies the number of resources to return per page. This parameter controls the size of each page in paginated responses.
  • pageCursor (<string>): A string value used to navigate through pages of results. This parameter holds the cursor for the current page, allowing the API to fetch the next set of results.

List Connected Integrations - Code Example

curl --location 'https://api.vanta.com/v1/integrations?pageSize=100&pageCursor=<string>' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer your_token'
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer your_token");

const requestOptions = {
  method: "GET",
  headers: myHeaders,
  redirect: "follow"
};

fetch("https://api.vanta.com/v1/integrations?pageSize=100&pageCursor=<string>", requestOptions)
  .then((response) => response.text())
  .then((result) => console.log(result))
  .catch((error) => console.error(error));
const axios = require('axios');

let config = {
  method: 'get',
  maxBodyLength: Infinity,
  url: 'https://api.vanta.com/v1/integrations?pageSize=100&pageCursor=<string>',
  headers: { 
    'Accept': 'application/json', 
    'Authorization': 'Bearer your_token'
  }
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
import requests

url = "https://api.vanta.com/v1/integrations?pageSize=100&pageCursor=<string>"

payload = {}
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer your_token'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)

List Connected Integrations - Example Response

{
    "results": {
        "pageInfo": {
            "endCursor": "em9vbQ==",
            "hasNextPage": false,
            "hasPreviousPage": false,
            "startCursor": "YWRkaWd5"
        },
        "data": [
            {
                "integrationId": "snowflake",
                "displayName": "Snowflake",
                "resourceKinds": [
                    "SnowflakeAccount",
                    "SnowflakeDatabase"
                ],
                "connections": [
                    {
                        "connectionId": "64dbda1a6491f66d234bfd41",
                        "isDisabled": true,
                        "connectionErrorMessage": "Vanta hit an unknown issue with Snowflake. Please reconnect the Snowflake integration to fix this."
                    }
                ]
            },            {
                "integrationId": "snyk",
                "displayName": "Snyk",
                "resourceKinds": [
                    "SnykAccount",
                    "SnykProject",
                    "SnykVulnerabilityV2"
                ],
                "connections": [
                    {
                        "connectionId": "65a0caf8b4c7501f891e2183",
                        "isDisabled": true,
                        "connectionErrorMessage": "Vanta failed to access some information in Snyk. Please reconnect the Snyk integration to fix this."
                    }
                ]
            },
            {
                "integrationId": "sumologic",
                "displayName": "Sumo Logic",
                "resourceKinds": [
                    "SumoLogicMonitor",
                    "SumoLogicUser"
                ],
                "connections": [
                    {
                        "connectionId": "645ab076f2e7f0df0be2da36",
                        "isDisabled": false,
                        "connectionErrorMessage": null
                    }
                ]
            },
        ]
    }
}


Get a List of Resources - By Resource-Kind and ConnectionId

Using the :resourceKind/resources endpoint, we can use the integrationId, connectionId, and resourceKind to fetch a list of resources from Vanta. This endpoint has several query parameters that can help filter the list down based on certain properties such as hasDescription, hasOwner, and isInScope.

Endpoint

/integrations/:integrationId/resource-kinds/:resourceKind/resources

Path Variables

  • integrationId (<string> - Required): The unique identifier for the integration. This parameter is necessary to specify which integration the request is targeting, and usually is just the integration name.
  • resourceKind (<string> - Required): The type of resource being requested. This parameter is necessary to specify the kind of resource for which details or a list of resources are being requested.

Query Parameters

  • connectionId (<string>): The unique identifier for the connection. This parameter is used to target a specific connection within an integration.
  • hasDescription (<boolean>): A boolean value to filter resources that have a description. When set to true, only resources with descriptions will be included in the response.
  • hasOwner (<boolean>): A boolean value to filter resources that have an owner. When set to true, only resources with owners will be included in the response.
  • isInScope (<boolean>): A boolean value to filter resources that are in scope. When set to true, only resources that are considered in scope will be included in the response.
  • pageSize (<number>): An integer value that specifies the number of resources to return per page.
  • pageCursor (<string>): A string value used to navigate through pages of results.

Since snowflakeDatabase resource types don't have descriptions, we'll only filter by isInScope and hasOwner:

/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources?connectionId=64dbda1a6491f66d234bfd41&hasOwner=false&isInScope=true&pageSize=10


Get List of Resources - Code Example

curl --location 'https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources?connectionId=64dbda1a6491f66d234bfd41&hasOwner=false&isInScope=true&pageSize=10' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer vat_YOUR_TOKEN'
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer vat_YOUR_TOKEN");

const requestOptions = {
  method: "GET",
  headers: myHeaders,
  redirect: "follow"
};

fetch("https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources?connectionId=64dbda1a6491f66d234bfd41&hasOwner=false&isInScope=true&pageSize=10", requestOptions)
  .then((response) => response.text())
  .then((result) => console.log(result))
  .catch((error) => console.error(error));
const axios = require('axios');

let config = {
  method: 'get',
  maxBodyLength: Infinity,
  url: 'https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources?connectionId=64dbda1a6491f66d234bfd41&hasOwner=false&isInScope=true&pageSize=10',
  headers: { 
    'Accept': 'application/json', 
    'Authorization': 'Bearer vat_YOUR_TOKEN'
  }
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});

import requests

url = "https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources?connectionId=64dbda1a6491f66d234bfd41&hasOwner=false&isInScope=true&pageSize=10"

payload = {}
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer vat_YOUR_TOKEN'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)


Get List of Resources - Response Example

{
    "results": {
        "pageInfo": {
            "endCursor": "eyJfaWQiOnsiJG9pZCI6IjY0ZGM4NDhiNjQ5MWY2NmQyM2M2NGUyNiJ9fQ==",
            "hasNextPage": false,
            "hasPreviousPage": false,
            "startCursor": "eyJfaWQiOnsiJG9pZCI6IjY0ZGM4NDhiNjQ5MWY2NmQyM2M2NGRmNiJ9fQ=="
        },
        "data": [
            {
                "responseType": "Database",
                "resourceKind": "SnowflakeDatabase",
                "resourceId": "64dc848b6491f66d23c64df6",
                "connectionId": "64dbda1a6491f66d234bfd41",
                "displayName": "SNOWFLAKE",
                "owner": null,
                "inScope": true,
                "description": "Snowflake Database",
                "creationDate": "2023-08-16T08:10:51.150Z",
                "account": "",
                "areBackupsEnabled": true,
                "isEncrypted": true,
                "containsEphi": null,
                "containsUserData": null
            },
            {
                "responseType": "Database",
                "resourceKind": "SnowflakeDatabase",
                "resourceId": "64dc848b6491f66d23c64e26",
                "connectionId": "64dbda1a6491f66d234bfd41",
                "displayName": "SNOWFLAKE_SAMPLE_DATA",
                "owner": null,
                "inScope": true,
                "description": "Snowflake Database",
                "creationDate": "2023-08-16T08:10:51.156Z",
                "account": "",
                "areBackupsEnabled": true,
                "isEncrypted": true,
                "containsEphi": null,
                "containsUserData": null
            }
        ]
    }
}

Get List of Resources - Response Schema

The response contains an array of resource objects, each object representing a separate resource. Below is the breakdown of each field within the objects:

  • responseType (string): Indicates the type of the resource. In this case, it is "Database".
  • resourceKind (string): Specifies the kind of resource. Here, it is "SnowflakeDatabase".
  • resourceId (string): The unique identifier for the resource.
  • connectionId (string): The unique identifier for the connection associated with the resource.
  • displayName (string): The display name of the resource.
  • owner (string or null): The ownerId of the resource. It can be null if no owner is assigned.
  • inScope (boolean): Indicates if the resource is in scope. When true, the resource is considered in scope.
  • description (string): A description of the resource.
  • creationDate (string): The date and time when the resource was created, in ISO 8601 format.
  • account (string): The account associated with the resource. It can be an empty string if no account is specified.
  • areBackupsEnabled (boolean): Indicates if backups are enabled for the resource. When true, backups are enabled.
  • isEncrypted (boolean): Indicates if the resource is encrypted. When true, the resource is encrypted.
  • containsEphi (boolean or null): Indicates if the resource contains electronic protected health information (ePHI). It can be null if this information is not specified.
  • containsUserData (boolean or null): Indicates if the resource contains user data. It can be null if this information is not specified.


Patch Resource-Kinds - Add Owner and Description

By using the Patch Resource-Kinds endpoint, We can update the resources with an appropriate Description and OwnerId of the Vanta User who's to own the resources. If you don't know the owner Id, you can find them via the List People endpoint, or from the URL on their profile via the People Page in Vanta:


Endpoint:

/integrations/:integrationId/resource-kinds/:resourceKind/resources

Path Variables

  • integrationId(required):<string>
    The integration identifier that the resources belong to.
  • resourceKind(required):<string>
    The string identifier of the different kinds of resources associated with this integration.

Request Body:

{
  "resourceIds": [
    "<string>"
  ],
  "inScope": "<boolean>",
  "description": "<string>",
  "ownerId": "<string>"
}

Patch Resource-Kinds - Code Example

curl --location --request PATCH 'https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer vat_YOUR_TOKEN' \
--data '{
  "resourceIds": [
    "64dc848b6491f66d23c64df6",
    "64dc848b6491f66d23c64e26"
  ],
  "description": "Test Description",
  "ownerId": "5fc82421a228f6b6f713547d" 
}'
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer vat_YOUR_TOKEN");

const raw = JSON.stringify({
  "resourceIds": [
    "64dc848b6491f66d23c64df6",
    "64dc848b6491f66d23c64e26"
  ],
  "description": "Test Description",
  "ownerId": "5fc82421a228f6b6f713547d"
});

const requestOptions = {
  method: "PATCH",
  headers: myHeaders,
  body: raw,
  redirect: "follow"
};

fetch("https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources", requestOptions)
  .then((response) => response.text())
  .then((result) => console.log(result))
  .catch((error) => console.error(error));
const axios = require('axios');
let data = JSON.stringify({
  "resourceIds": [
    "64dc848b6491f66d23c64df6",
    "64dc848b6491f66d23c64e26"
  ],
  "description": "Test Description",
  "ownerId": "5fc82421a228f6b6f713547d"
});

let config = {
  method: 'patch',
  maxBodyLength: Infinity,
  url: 'https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources',
  headers: { 
    'Content-Type': 'application/json', 
    'Accept': 'application/json', 
    'Authorization': 'Bearer vat_YOUR_TOKEN'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});

import requests
import json

url = "https://api.vanta.com/v1/integrations/snowflake/resource-kinds/SnowflakeDatabase/resources"

payload = json.dumps({
  "resourceIds": [
    "64dc848b6491f66d23c64df6",
    "64dc848b6491f66d23c64e26"
  ],
  "description": "Test Description",
  "ownerId": "5fc82421a228f6b6f713547d"
})
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer vat_YOUR_TOKEN'
}

response = requests.request("PATCH", url, headers=headers, data=payload)

print(response.text)


Patch Resource-Kinds- Example Response

204 No Content


Viewing the updated resources in Vanta

Now that we've successfully added an Active owner to the SNOWFLAKE and SNOWFLAKE_SAMPLE_DATA databases, they're no longer failing the Inventory items have active owners test:

We've also added a Description to these resources, but since they already had one it simply updated it instead. You can see that reflected on the Inventory Page: