All insights

Unity CatalogGovernanceDatabricks5 Minute Features3 min read

Tag Propagation in Unity Catalog

Kristian Johannesen

Tag Propagation carries governed tags from a table or column to the objects built from it, so a derived table is classified, and protected, like its source.

Example: a table created with CREATE TABLE AS SELECT from a customers table tagged as sensitive is tagged as sensitive too, so the policies that mask its PII apply to the copy as well.

Where to find it: Catalog Explorer, under Governed Tags: select a tag and choose Configure.

How tags follow the data

Without Tag Propagation, governed tags stay on the object they were assigned to. That is a real gap, and one I have seen worry security teams at several companies. Anyone allowed to create tables can run CREATE TABLE AS SELECT * FROM customers, and the copy carries neither the tags nor the access policies that depend on them. The masked names and email addresses are readable again.

Tag Propagation closes that gap for governed tags. It is switched on per tag, and only works for governed ones: custom tags never propagate.

Configuration is a toggle on the tag and a default propagation value, set by someone with MANAGE on the tag. Tag Propagation itself runs as a system operation, so nobody needs ASSIGN for it to happen.

Operations that propagate tags, each copying them to the object it creates or writes to:

-- A new table from a query: the demo's CTAS
CREATE TABLE uc_demo.sample.customers_by_region AS
SELECT
  customers.region,
  count(DISTINCT customer_id) AS customer_count,
  sum(lifetime_value)         AS total_lifetime_value,
  max(user_email)             AS region_responsible
FROM uc_demo.sample.customers
LEFT JOIN uc_demo.sample.user_region_map
  ON customers.region = user_region_map.region
GROUP BY customers.region;

-- A view: also ALTER VIEW and CREATE OR REPLACE VIEW
CREATE VIEW uc_demo.sample.customers_by_region_v AS
SELECT * FROM uc_demo.sample.customers_by_region;

-- Writes into an existing table
INSERT INTO uc_demo.sample.customers_by_region SELECT ...;
MERGE INTO uc_demo.sample.customers_by_region USING ...;

-- A clone: table-level tags only
CREATE TABLE uc_demo.sample.customers_copy DEEP CLONE uc_demo.sample.customers;

Table and column tags both propagate. A table-level tag goes to the derived table or view, and a column-level tag to the matching column in it. Tag Propagation finds those by following the lineage the operation records.

Conflicts arise when a target would get the same tag with different values, from several sources or from a value already on the target. The tag's default propagation value then decides.

Where it stops

  • Serverless only. On classic compute, no tags propagate.
  • Tables and views only. Volumes are not covered, nor are streaming tables and materialised views created by Lakeflow Declarative Pipelines.
  • Not instant. Tag Propagation runs asynchronously after the operation, usually within minutes. Until the tag arrives, policies that reference it are not enforced on the new object.
  • 50 tags per object, propagated ones included.

Catalog Explorer shows whether each tag on an object was assigned directly, inherited, or applied automatically, for example by a Tag Automation.

Start testing it today

If your access policies depend on governed tags, Tag Propagation closes a gap you otherwise have to police by hand. I would start testing it on every tag that protects data.

Mind the delay, though. For the minutes before the tag arrives, a new table is potentially not protected. I am not sure yet what the cleanest way around that is. A short wait before granting access to new objects may be one. I hope the window shrinks over time, because tags that arrive instantly would be a big tick on the security side.

The full reference is Databricks' documentation: Tag propagation.

A conflict on purpose

The video uses a governed tag called sensitive, a table-level true or false set from the PII tags on a table's columns. The customers table is tagged sensitive: true. A second table, user_region_map, is tagged sensitive: false, to force a conflict. A CREATE TABLE AS SELECT on a serverless starter warehouse joins the two on region and aggregates them, much like a table in a gold layer: customers, total lifetime value and a responsible person per region.

The new table, customers_by_region, had its tag almost immediately, faster than the documentation promises. With true and false coming from upstream, it resolved to true, the default set on the tag.

Column-level lineage also held up better than I expected. The region, the distinct count of customer IDs and the region's responsible person all traced back to their source columns, so column tags on those would have carried through. Lineage has struggled with complex column transformations in the past, so check the result on your own, but it has clearly improved.

Plays from YouTube, which may set cookies once it starts. Watch on YouTube (opens in a new tab)

More insights

Let’s discuss your next data project

Distinkt Consulting A/S only stores and processes the details you submit in order to reply to your enquiry. Privacy policy

Torben Kornum, Partner & CEO

Prefer to call or email?
Contact us directly