> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vantage.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# VQL for Virtual Tags

> Learn how to use VQL to define Virtual Tag values, cost allocations, and collapsed tag keys in the Vantage API and Terraform provider.

Virtual Tags use VQL to determine which costs receive a value or allocation. Virtual Tag filters use the same `costs` and `tags` schema, keywords, and syntax as Cost Reports.

<Tip>
  To build a Virtual Tag filter in the console, navigate to the [**Tags** page](https://console.vantage.sh/settings/tags), create or edit a Virtual Tag, and add a value. In an input or output costs filter, click **View as VQL**, then **Edit VQL**, to edit the query directly.
</Tip>

You must have the Organization Owner role to edit Virtual Tag VQL in the console.

## Where Virtual Tags Use VQL

A Virtual Tag configuration can contain VQL in the following locations:

| Location                                                  | API or Terraform field        | Purpose                                                                                                                     |
| --------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [Value output filter](#value-output-filters)              | `values[].filter`             | Identifies the costs that receive a custom value or that are allocated using a business metric, cost metric, or percentages |
| [Cost-based input filter](#cost-based-allocation-filters) | `values[].cost_metric.filter` | Identifies the input costs used to calculate the allocation percentages                                                     |
| [Collapsed tag key filter](#collapsed-tag-key-filters)    | `collapsed_tag_keys[].filter` | Limits the costs for which values from a collapsed tag key are copied into the Virtual Tag                                  |

The API examples on this page are request bodies for [`POST /v2/virtual_tag_configs`](https://docs.vantage.sh/api/virtualtags/create-virtual-tag-config).

The order of objects in `values` determines precedence among value filters. When more than one value filter matches a cost, Vantage applies the first matching value. Value filters also take precedence over [collapsed tag keys](#collapsed-tag-key-filters).

## Schema

The schema below is the same one used for [Cost Reports](/vql_cost_report).

VQL comprises two namespaces: `costs` and `tags`, which represent the available [filters](/cost_reports#filter-cost-reports) on Cost Reports in the Vantage console. Reference a filter as `namespace.field`, such as `costs.provider` or `tags.name`.

<Info>
  Availability of the fields listed below varies among different cloud providers. For a comprehensive list of available fields per provider, including the source billing field each one maps to, see the [Data Dictionary](/data_dictionary).
</Info>

| Namespace | Field                 | Description                                           | Example                                                                                                                                  |
| --------- | --------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `costs`   | `provider` (optional) | Provider that generated the cost                      | [Filter across all providers](/vql_cost_report#filter-across-all-providers) or [combine providers](/vql_cost_report#combining-providers) |
| `costs`   | `allocation`          | Fraction of a cost allocated to the result            | [Cost allocation](/vql_cost_report#cost-allocation)                                                                                      |
| `costs`   | `region`              | Provider region associated with the cost              | [Regions](/vql_cost_report#costs-from-a-list-of-regions)                                                                                 |
| `costs`   | `marketplace`         | Whether an AWS cost is a Marketplace transaction      | [Marketplace transactions](/vql_cost_report#get-marketplace-transactions)                                                                |
| `costs`   | `account_id`          | Resource account, project, or subscription identifier | [Account ID](/vql_cost_report#costs-by-account-id)                                                                                       |
| `costs`   | `provider_account_id` | Billing account identifier                            | [Provider account ID](/vql_cost_report#costs-by-provider-account-id)                                                                     |
| `costs`   | `service`             | Normalized provider service name                      | [Service](/vql_cost_report#per-resource-costs-and-costs-by-service)                                                                      |
| `costs`   | `category`            | Normalized cost category                              | [Category](/vql_cost_report#costs-by-specific-category)                                                                                  |
| `costs`   | `subcategory`         | Provider-specific cost subcategory                    | [Subcategory](/vql_cost_report#costs-by-specific-subcategory)                                                                            |
| `costs`   | `resource_id`         | Provider identifier for a resource                    | [Resource ID](/vql_cost_report#per-resource-costs-and-costs-by-service)                                                                  |
| `costs`   | `charge_type`         | Normalized charge type, such as usage or credit       | [Charge type](/vql_cost_report#cost-by-charge-type)                                                                                      |
| `costs`   | `usage_unit`          | Unit used to measure usage                            | [Usage unit](/vql_cost_report#filter-by-usage-unit)                                                                                      |
| `tags`    | `name`                | Provider or Virtual Tag key                           | [Tags](/vql_cost_report#filter-by-tag)                                                                                                   |
| `tags`    | `value`               | Value associated with a tag key                       | [Untagged costs](/vql_cost_report#filter-for-untagged-resources)                                                                         |

<Info>
  `costs.provider` is optional. Omit it to filter across every connected provider in a single query—see [Filter Across All Providers](/vql_cost_report#filter-across-all-providers). Provider-specific fields (such as AWS `costs.marketplace`) only apply when `costs.provider` is included for that provider.
</Info>

## Keywords

The keywords below are the same ones used for [Cost Reports](/vql_cost_report).

VQL includes a set of keywords to create complex filter conditions. These keywords function similar to their SQL equivalents.

| Keyword | Description                                    | VQL Sample                                                                            | Explanation                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------- | ---------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `AND`   | Logical AND operator                           | `costs.provider = 'aws' AND costs.service = 'Amazon Elastic Compute Cloud - Compute'` | This example filters AWS costs for the Amazon Elastic Compute Cloud - Compute service, where both conditions must be true.                                                                                                                                                                                                                                                                                                                                                                                   |
| `OR`    | Logical OR operator                            | `costs.provider = 'azure' OR costs.provider = 'aws'`                                  | This example retrieves costs from either Azure or AWS. At least one condition must be true.                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `IN`    | Used to compare against an array list          | `costs.provider = 'azure' AND costs.account_id IN ('account-1', 'account-2')`         | This example filters based on a list of account IDs, returning data for the specified accounts.  <br /><br />    You can also use `IN` along with a special syntax for filtering by multiple tags. See [Filter by Multiple Tags](/vql_cost_report#filter-by-multiple-tags) for details. <br /><br /> `costs.provider IN (...)` is supported and is expanded into one filter set per provider. `costs.provider NOT IN (...)` is not supported.                                                                |
| `LIKE`  | Performs string comparisons                    | `costs.provider = 'gcp' AND tags.name = 'environment' AND tags.value LIKE '%prod%'`   | This example selects data where the tag value contains `prod`, such as `production-1`.                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `NOT`   | Represents negation                            | `costs.provider = 'aws' AND costs.region NOT IN ('us-east-1', 'us-east-2')`           | This example filters out data from both specified regions, providing all AWS costs *not* in these regions. Use `NOT IN` to specify a list of single or multiple values.   <br /><br />   You can also use the `!=` or `<>` operators for "is not."  <br /><br />    `costs.provider = 'aws' AND costs.region != 'us-east-1'`  <br /><br />    You can use `NOT LIKE` to perform string comparisons:   <br /><br />   `costs.provider = 'gcp' AND tags.name = 'environment' AND tags.value NOT LIKE '%prod%'` |
| `~*`    | Flexible match operator for tag values         | `costs.provider = 'aws' AND (tags.name = 'teams' AND tags.value ~* 'Team A')`         | Searches for all items where the tag value loosely matches `Team A`, ignoring case, whitespace, hyphens, and punctuation.                                                                                                                                                                                                                                                                                                                                                                                    |
| `!~*`   | Negated flexible match operator for tag values | `costs.provider = 'aws' AND (tags.name = 'teams' AND tags.value !~* 'Team A')`        | Filters out all items where the tag value loosely matches `Team A`, ignoring case, whitespace, hyphens, and punctuation.                                                                                                                                                                                                                                                                                                                                                                                     |

With these keywords, you can construct complex filter conditions in VQL, providing flexibility and precision when querying and analyzing cloud cost data.

## Syntax

The syntax below is the same one used for [Cost Reports](/vql_cost_report).

You can think of VQL in its current iteration as the `WHERE` clause of a SQL query. By combining the schema and keywords above with parentheses, you can form complex filter operations, such as:

```vql theme={null}
(costs.provider = 'mongo' AND costs.allocation = 1.0 AND (costs.service = 'REALM' AND costs.resource_id IN ('s3'))) OR (costs.provider = 'aws' AND costs.allocation = 1.0 AND costs.account_id IN ('123456789012'))
```

## Value Output Filters

Every object in `values` requires a `filter`. For a custom value, the filter identifies the costs assigned that value. For business metric, cost-based, and percent-based values, it identifies the output costs to allocate.

The following examples map AWS accounts to team values. Because `Platform` appears first, it takes precedence if the filters overlap.

<Tabs>
  <Tab title="API">
    ```json theme={null}
    {
      "key": "Team",
      "overridable": false,
      "values": [
        {
          "name": "Platform",
          "filter": "costs.provider = 'aws' AND costs.account_id IN ('111111111111', '222222222222')"
        },
        {
          "name": "Data",
          "filter": "costs.provider = 'aws' AND costs.account_id = '333333333333'"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```terraform theme={null}
    resource "vantage_virtual_tag_config" "team" {
      key         = "Team"
      overridable = false

      values = [
        {
          name   = "Platform"
          filter = "costs.provider = 'aws' AND costs.account_id IN ('111111111111', '222222222222')"
        },
        {
          name   = "Data"
          filter = "costs.provider = 'aws' AND costs.account_id = '333333333333'"
        }
      ]
    }
    ```
  </Tab>
</Tabs>

### Business Metric and Percentage Output Filters

Business metric and percent-based values also use `values[].filter` as their output costs filter:

<Tabs>
  <Tab title="API">
    ```json theme={null}
    {
      "key": "Allocated Team",
      "overridable": false,
      "values": [
        {
          "business_metric_token": "bsnss_mtrc_1234",
          "label_key": "team",
          "filter": "costs.provider = 'aws' AND costs.service = 'Amazon Elastic Compute Cloud - Compute'"
        },
        {
          "display_name": "Shared support",
          "filter": "costs.provider = 'aws' AND costs.service = 'AWS Support (Business)'",
          "percentages": [
            { "value": "Platform", "pct": 60 },
            { "value": "Data", "pct": 40 }
          ]
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```terraform theme={null}
    resource "vantage_virtual_tag_config" "allocated_team" {
      key         = "Allocated Team"
      overridable = false

      values = [
        {
          business_metric_token = "bsnss_mtrc_1234"
          label_key             = "team"
          filter                = "costs.provider = 'aws' AND costs.service = 'Amazon Elastic Compute Cloud - Compute'"
        },
        {
          display_name = "Shared support"
          filter       = "costs.provider = 'aws' AND costs.service = 'AWS Support (Business)'"
          percentages = [
            { value = "Platform", pct = 60 },
            { value = "Data", pct = 40 }
          ]
        }
      ]
    }
    ```
  </Tab>
</Tabs>

<Note>
  `label_key` selects the [label column](/business_metrics#labeled-business-metrics) whose values become the tag values, and it's required for business metrics that support named label columns. Business metrics created before multiple label columns were supported use a single `label` column; omit `label_key` for those metrics, because Vantage rejects it.
</Note>

### Normalize Values Across Tag Keys

Use a value filter to map inconsistent provider tag keys to one Virtual Tag value. Repeat the provider condition in each `OR` branch so each branch forms a complete filter set:

```sql theme={null}
(costs.provider = 'aws' AND tags.name = 'environment' AND tags.value = 'production') OR (costs.provider = 'aws' AND tags.name = 'env' AND tags.value = 'prod') OR (costs.provider = 'aws' AND tags.name = 'stage' AND tags.value = 'production')
```

## Cost-Based Allocation Filters

A cost-based value uses two VQL filters:

* `values[].filter` selects the output costs to allocate.
* `values[].cost_metric.filter` selects the input costs used to calculate the allocation percentages.

For most cost-based allocations, include `costs.charge_type = 'Usage'` in the input filter so credits, taxes, and fees do not affect the allocation.

<Tabs>
  <Tab title="API">
    ```json theme={null}
    {
      "key": "Team",
      "overridable": false,
      "values": [
        {
          "display_name": "Allocate shared infrastructure",
          "filter": "costs.provider = 'aws' AND costs.service = 'Amazon Elastic Compute Cloud - Compute'",
          "cost_metric": {
            "filter": "costs.provider = 'aws' AND costs.charge_type = 'Usage' AND tags.name = 'team'",
            "aggregation": {
              "tag": "team"
            }
          }
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```terraform theme={null}
    resource "vantage_virtual_tag_config" "team_allocation" {
      key         = "Team"
      overridable = false

      values = [
        {
          display_name = "Allocate shared infrastructure"
          filter       = "costs.provider = 'aws' AND costs.service = 'Amazon Elastic Compute Cloud - Compute'"
          cost_metric = {
            filter = "costs.provider = 'aws' AND costs.charge_type = 'Usage' AND tags.name = 'team'"
            aggregation = {
              tag = "team"
            }
          }
        }
      ]
    }
    ```
  </Tab>
</Tabs>

## Collapsed Tag Key Filters

[Tag Key Collapsing](/tagging#tag-key-collapsing) copies the values of an existing tag key, either a provider tag or another Virtual Tag, into this Virtual Tag. Use `collapsed_tag_keys[].filter` to restrict which costs are eligible.

When `filter` is set, do not also set `providers`. Put provider restrictions directly in the VQL filter. If you need only provider-level scoping and no other conditions, use `providers` instead of `filter`.

Collapsed tag keys follow these precedence rules:

* If a value filter and a collapsed tag key both match a cost, the value filter's value is applied.
* If more than one collapsed tag key matches a cost, the key listed first is applied.

<Tabs>
  <Tab title="API">
    ```json theme={null}
    {
      "key": "Environment",
      "overridable": false,
      "collapsed_tag_keys": [
        {
          "key": "environment",
          "filter": "costs.provider = 'aws' AND costs.account_id IN ('111111111111', '222222222222')"
        },
        {
          "key": "env",
          "filter": "costs.provider = 'gcp'"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```terraform theme={null}
    resource "vantage_virtual_tag_config" "environment" {
      key         = "Environment"
      overridable = false

      collapsed_tag_keys = [
        {
          key    = "environment"
          filter = "costs.provider = 'aws' AND costs.account_id IN ('111111111111', '222222222222')"
        },
        {
          key    = "env"
          filter = "costs.provider = 'gcp'"
        }
      ]
    }
    ```
  </Tab>
</Tabs>

## Nested Virtual Tags

To reference an existing Virtual Tag, use its key and value with the `tags` namespace, just as you would use a provider tag:

```sql theme={null}
costs.provider = 'aws' AND tags.name = 'Environment' AND tags.value = 'Production'
```

This example can be used as the output filter for a second Virtual Tag. Vantage evaluates nested tags in dependency order.

Nested Virtual Tags cannot contain cyclical references, such as Tag A referencing Tag B while Tag B references Tag A. A filter that references the Virtual Tag's own key isn't a cycle; it matches the provider tag with the same key. If a nested relationship includes cost allocation, every referenced allocated tag must belong to the same provider-specific allocation chain. See [Nested Virtual Tags](/tagging#nested-virtual-tags) for configuration details and limitations.

## Performance Guidelines

* Scope a filter to the providers it needs. A filter without `costs.provider` is evaluated against every connected provider.
* Prefer exact matches with `=` and `IN` over `LIKE`, `NOT LIKE`, `~*`, and `!~*`.
* When a filter applies to only a few providers, use a complete provider-specific expression in each `OR` branch.
* Put high-volume value rules first so most costs match early.

See [Best Practices for Filtering Across All Providers](/tagging#best-practices-for-filtering-across-all-providers) for more guidance.

## Troubleshooting

* Wrap string values in single quotes, not double quotes.
* Do not use a saved filter to define a Virtual Tag value. Enter or paste the VQL expression directly.
* Do not combine `providers` and `filter` on the same collapsed tag key.
* If multiple value filters match the same cost, reorder the values so the intended value appears first.
* If a nested tag fails validation, check for a cyclical reference between tags or a reference to an allocated tag outside the same provider-specific allocation chain.
* `costs.provider IN (...)` is supported, but `costs.provider NOT IN (...)` is not.
* For invalid VQL, Vantage returns a `filter:` error. An invalid cost-based input filter returns a `cost metrics filter:` error.
