data2al icon data2al Snowflake, Databricks, and SQL Engineering

Concept note

Govern Data in Unity Catalog

A practical reference for Unity Catalog data governance: the difference between managed and external tables, GRANT/REVOKE/DENY privilege rules, column-level masking, row-level security with row filters, and attribute-based access control.

2026-06-17
Alan
Databricks
Intermediate
Databricks Unity-Catalog Governance Security ABAC Row-Level-Security

Unity Catalog handles permissions and data governance across all workspaces in a Databricks account. Access control is defined on a hierarchy of securable objects — account, catalog, schema, table, column — and privileges flow down unless explicitly restricted.

Managed vs External tables

The distinction between managed and external tables is about who controls the data lifecycle.

Managed tables: Unity Catalog owns both the metadata and the underlying storage. When you drop a managed table, the data files in cloud storage are deleted.

CREATE TABLE prod.silver.orders (
    order_id BIGINT,
    customer_id BIGINT,
    amount DECIMAL(18,2),
    order_ts TIMESTAMP
) USING DELTA;

External tables: Unity Catalog manages the metadata, but the data files live in a cloud storage path you control. Dropping an external table removes the metadata from Unity Catalog; the files in storage are left intact.

CREATE TABLE prod.silver.orders_external
LOCATION 's3://my-bucket/silver/orders/'
USING DELTA;

Use managed tables when you want Unity Catalog to handle storage. Use external tables when the data needs to be accessible outside Databricks (other tools reading the same path) or when the storage lifecycle is managed separately.

GRANT, REVOKE, and DENY

Unity Catalog uses a standard SQL privilege model. Privileges are granted on securable objects and inherited by objects below them in the hierarchy.

-- grant SELECT on a table to a group
GRANT SELECT ON TABLE prod.silver.orders TO GROUP analysts;

-- grant all privileges on a schema to a service principal
GRANT ALL PRIVILEGES ON SCHEMA prod.silver TO `my-service-principal@company.com`;

-- revoke a privilege
REVOKE SELECT ON TABLE prod.silver.orders FROM GROUP analysts;

DENY overrides GRANT. If a user belongs to two groups — one with SELECT granted and one with SELECT denied — the deny wins:

DENY SELECT ON TABLE prod.silver.pii_orders TO GROUP contractors;

The privilege hierarchy: granting SELECT on a catalog gives SELECT on all schemas and tables within it. Granting SELECT on a schema gives SELECT on all tables in that schema. A specific GRANT or DENY at the table level overrides the inherited permission for that table only.

Column-level masking

Column masking hides or transforms sensitive column values for specific users or groups without removing the column from the schema. The mask is a SQL function applied transparently at query time.

CREATE FUNCTION prod.governance.mask_ssn(ssn STRING)
RETURNS STRING
RETURN CASE
    WHEN IS_ACCOUNT_ADMIN() THEN ssn
    WHEN IS_MEMBER('pii_access') THEN ssn
    ELSE CONCAT('***-**-', RIGHT(ssn, 4))
END;

ALTER TABLE prod.silver.customers
ALTER COLUMN ssn
SET MASK prod.governance.mask_ssn;

Once the mask is applied, any SELECT on customers.ssn runs the function. Users in the pii_access group or account admins see the real value; everyone else sees ***-**-XXXX.

To remove the mask:

ALTER TABLE prod.silver.customers
ALTER COLUMN ssn DROP MASK;

Row-level security with row filters

Row filters restrict which rows a user can see in a table. The filter is a SQL function that returns a boolean expression. Only rows where the expression is true are visible to the querying user.

CREATE FUNCTION prod.governance.filter_by_region(region_col STRING)
RETURNS BOOLEAN
RETURN IS_ACCOUNT_ADMIN() OR region_col = SESSION_CONTEXT('region');

ALTER TABLE prod.silver.orders
SET ROW FILTER prod.governance.filter_by_region ON (region);

Account admins see all rows. Other users see only rows where the region column matches the value in their session context (set by an identity provider or login attribute).

To remove the filter:

ALTER TABLE prod.silver.orders DROP ROW FILTER;

Attribute-Based Access Control

ABAC extends the GRANT/DENY model by making access decisions based on user attributes rather than explicit group membership. In Unity Catalog, this is implemented through masking functions and row filters that reference built-in session functions:

Function Returns
current_user() The email address of the querying user
IS_ACCOUNT_ADMIN() True if the user is an account admin
IS_MEMBER('group') True if the user belongs to the named group
SESSION_CONTEXT('key') Value of a session attribute set during login

A practical ABAC pattern — users can only see their own rows in a shared table:

CREATE FUNCTION prod.governance.own_records_only(owner_email STRING)
RETURNS BOOLEAN
RETURN owner_email = current_user() OR IS_ACCOUNT_ADMIN();

ALTER TABLE prod.silver.user_activity
SET ROW FILTER prod.governance.own_records_only ON (owner_email);

ABAC policies built from these functions do not require re-granting when users change roles — the function evaluates the user’s current attributes at query time.


Similar Posts