# Tagging policies

> Enforce a tag key across your resources. Derive its value from each resource with a template or lookup table, check existing tags against your rules, and manage policies in bulk from Excel.

Source: https://zop.dev/docs/zopnight/policies/tagging

---

A **tagging policy** enforces one tag key across your resources. It can **set the value** (derive it from each resource's own fields or tags), **check the tag** (flag resources where it's missing, has a value you don't allow, or shouldn't exist), or both.

![Tagging policies list with Download template, Export, and Import icon buttons beside + New policy, and policies such as Cost allocation deriving provider-region values for the cost-allocation key](https://storage.googleapis.com/zopdev-blog-resources/1/files/originals/20261020/32c17a97-e22a-43aa-92f4-0521cb215e8f-policiestagging.png)

*Governance → Policies → Tagging: each policy with its tag key, derived value, scope, and status.*

Derived values become [Smart Tags](https://zop.dev/docs/zopnight/concepts/smart-tags): virtual tags used for cost attribution inside ZopNight. Nothing changes in your cloud account unless you choose to [apply a tag to the cloud](https://zop.dev/docs/zopnight/concepts/smart-tags#applying-tags-to-the-cloud).

You manage tagging policies at **Governance → Policies → Tagging**.

## Before you start

- **Permission to manage policies and to view Smart Tags.** The form uses Smart Tags to check your policy. The built-in **Editor** and **Admin** roles have both; **Viewer** can only look. See [Scopes](https://zop.dev/docs/zopnight/concepts/scopes).
- **Discovered resources.** Values come from fields ZopNight already holds for every resource, so you don't need any cloud tags to start.

## Create a tagging policy

**Open Governance → Policies → Tagging**

  Click **+ New policy**. The drawer opens as **New tagging policy**.

**Name it**

  A **Name** (required, up to 120 characters, for example "Cost-center tagging") and, if you like, a **Description**.

**Choose where it applies**

  **Whole organisation** or **One cloud account**. An account policy overrides an organisation policy for the same tag key. You can't change this after saving.

**Choose which resources**

  By default the policy covers every resource in scope. Click **Narrow with a condition** to limit it. Match **ALL of** or **ANY of** your conditions:

  | Field | Operators |
  |---|---|
  | Resource types | is, is any of |
  | Region | is, is any of |
  | Cloud account (the provider: AWS, GCP, or Azure) | is, is any of |
  | Instance type | is, is any of |
  | Status | is, is any of |
  | Name | is, contains (at least 3 characters), regex |
  | Resource group (Azure) | is, is any of, contains, regex, exists |
  | Tag / label | exists, is, is not |

**Set the tag to enforce**

  Enter the **Tag key** (up to 128 characters, for example `cost-center`). Then fill in **Set the value**, **Check the tag**, or both. The **Preview** line shows your template as you type (placeholders aren't filled in yet).

**Validate, then create**

  Click **Validate**. Once it passes, **Create policy** turns on. Any later change asks you to validate again.

Derived values and violations are worked out again within seconds of saving, and on every discovery refresh after that.

## Set the value

Write the **Value** as a template. Plain text is used as written; anything in braces is filled in per resource:

| Placeholder | Filled with |
|---|---|
| `{Region}` | The resource's region |
| `{Type}` | Its resource type |
| `{Name}` | Its name |
| `{Provider}` | `aws`, `gcp`, or `azure` |
| `{InstanceType}` | Its instance type or size |
| `{ResourceGroup}` | Its Azure resource group |
| `{Team}`, `{env}`, … (any other name) | The value of the resource's existing tag with that key |
| `{tag:Region}` | The existing tag called `Region` (use this when a tag key matches a placeholder name) |

Placeholder names are case-sensitive: `{Region}` is the region, but `{region}` reads a tag called `region`.

For example, `cc-{Team}-{Region}` gives `cc-payments-us-east-1`. When a placeholder is empty, the dash or underscore next to it is dropped, so a resource with no `Team` tag gets `cc-us-east-1` rather than `cc--us-east-1`.

Two options refine the value:

- **Look up in a table.** The template becomes the input, and you map each **Input value** to an **Output value** (click **+ Add mapping** for more rows). Inputs match regardless of case. Use this to turn, say, account names into cost centres.
- **If empty use.** A fallback value (up to 256 characters) for when the template comes out empty, the lookup has no match, or the value is one the cloud won't accept (see below). Leave it blank to skip those resources.

Extra spaces at the start and end are removed. On GCP, values are also made lowercase and cut to 63 characters (AWS and Azure allow 256). GCP accepts only lowercase letters, digits, `-`, and `_`, so a value with anything else (a space, `.`, `:`, `/`, `=`, `+`, `@`, and so on) is replaced by your **If empty use** value, or skipped for that resource if you haven't set one. **Keep casing** keeps the value exactly as you wrote it; on GCP, a value with capital letters is then treated like any other value GCP won't accept.

A policy reads a resource's real tags, not values derived by other policies, so one tagging policy can't build on another's output.

## Check the tag

**Check the tag** turns the policy into a rule. ZopNight checks every resource in scope and raises a violation wherever the tag doesn't comply:

| Setting | Violation raised |
|---|---|
| **Required**: the tag must be present with a value | `missing_required` |
| **Allowed values**: a comma-separated list, for example `prod, staging, dev` | `value_not_allowed` |
| **Forbidden**: the tag must not exist | `forbidden` |

**Forbidden** can't be combined with the other two. Allowed values match exactly on AWS and Azure (`PROD` isn't `prod`) and regardless of case on GCP.

Violations show on the **Tag Compliance** card of the Smart Tags page. A policy that both sets and checks a value fills in the gap and validates the result: accepting the derived Smart Tag clears a `missing_required` violation. See [Tag compliance](https://zop.dev/docs/zopnight/concepts/smart-tags#tag-compliance).

## When several policies share a key

You can point many policies at the same tag key, each with its own condition. This turns the key into a decision table: for example, one row per name pattern to fill in an `Application` tag.

For each resource and tag key, one policy wins, in this order:

1. **The narrowest scope**: a cloud-account policy beats an organisation policy.
2. **Priority**: lower numbers win. Priority comes from the row order of an [Excel import](#manage-policies-in-excel). Policies created in the form have no priority, so imported rows win over them.
3. **The oldest policy.**

Setting a value and checking a tag are decided separately. A policy that only checks a tag never replaces one that sets its value, and the other way round.

## Manage policies in Excel

For a decision table or a large estate, manage policies in a spreadsheet. The three icon buttons beside **+ New policy** are:

- **Download template**: a blank workbook with a README, a **Tag Rules** sheet, a **Lookup Tables** sheet, and a **Reference** sheet listing your account names and resource types.
- **Export to Excel**: your current policies, with their Policy IDs filled in, so editing and re-importing updates them.
- **Import from Excel**: upload an `.xlsx` or `.xls` file.

Each row of **Tag Rules** is one policy. Only **Tag Key** is required; rows whose key starts with `#` are examples and are skipped. The other columns are **Policy ID**, **Action** (create or update, or delete), **Value**, **Look up in** (a table from the Lookup Tables sheet), **Default value**, **Preserve case sensitivity**, **Requirement** (required or forbidden), **Value must be one of**, **Resource types**, **Condition**, **Scope to apply** (account names, blank for the whole organisation), **Rule name**, **Notes**, and **Active**.

A **Condition** is written as an expression, for example `Region = ap-south-1 AND tag:Team exists`. It can use `Region`, `Type`, `Name`, `Provider`, `InstanceType`, `ResourceGroup`, `Status`, `Account` (the account's ID, not its name), and `tag:<key>`, with `=`, `!=`, `contains`, `matches`, `exists`, and `is one of a, b`, combined with `AND`, `OR`, `NOT`, and brackets.

To limit a rule to certain accounts, put the account names in **Scope to apply**. One rule is created per account.

After upload, **Import tagging policies** previews every row as new, update, or delete. Open a row to edit it in the full form, or remove it. Rows match existing policies by Policy ID, or by tag key, scope, and condition. Tick the option to delete existing policies for the same keys that no row matches, then click **Apply**. Each row reports Created, Updated, Deleted, or why it failed.

## Manage policies

The list shows each policy's name, tag key, derived value, scope (Org or Account), and a **Status** switch. Search by name or tag key. Search only looks at the page you're on, so if you have many policies, set the list to 50 or 100 per page first.

- **Turn a policy off** with its switch.
- **Edit** by clicking the row (or **Edit** in the row menu).
- **Delete** from the row menu. This can't be undone, and its derived tags are dropped.

## From the API or an AI assistant

Tagging policies live in the shared policy API (`/policies` with `domain=tagging`). Check a policy before saving it with `POST /tagging/policies/validations`, and list violations with `GET /tagging/violations`.

From an AI assistant, use the [MCP server](https://zop.dev/docs/zopnight/integrations/mcp) tools `list_tagging_policies`, `get_tagging_policy_catalog`, `create_tagging_policy`, `update_tagging_policy`, `delete_policy`, and `list_tagging_violations` (to see resources that break your tag rules).
