<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic One Policy to Mask Them All: ABAC + VARIANT in Unity Catalog in Community Articles</title>
    <link>https://community.databricks.com/t5/community-articles/one-policy-to-mask-them-all-abac-variant-in-unity-catalog/m-p/150648#M1066</link>
    <description>&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;Databricks ABAC lets you apply a single schema-level policy across columns of any data type — no more managing one mask function per type. Here's ho&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;w to use the VARIANT data type to make it work.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;If you've implemented column masking in Unity Catalog, you've probably run into this friction: every column type needs its own mask function. A STRING column needs a STRING-returning UDF. A DATE column needs a DATE-returning UDF. Multiply that across a schema with dozens of sensitive columns and you're managing a small army of functions and policies.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;There's a better way. By combining &lt;/SPAN&gt;&lt;STRONG&gt;ABAC (Attribute-Based Access Control)&lt;/STRONG&gt;&lt;SPAN&gt; with the &lt;/SPAN&gt;&lt;STRONG&gt;VARIANT data type&lt;/STRONG&gt;&lt;SPAN&gt;, you can write one function and one policy that handles every column type automatically.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;The Setup&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Create a test table. In this example it’s the employees table.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE TABLE abac_variant_test_catalog.abac_test.employees (
  id INT,
  name STRING,
  salary DOUBLE,
  birthdate DATE,
  dept STRING -- control column, not masked)
COMMENT 'The table contains records of employees, including relevant details such as their names, salaries, birthdates, and departments. Possible use cases include analyzing salary distributions, tracking employee demographics, and evaluating departmental staffing levels.'&lt;/LI-CODE&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Tag your sensitive columns with a governed tag — no matter what type they are:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_0-1773281812229.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24736i7A3BDA1B26CE808A/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_0-1773281812229.png" alt="MoJaMa_0-1773281812229.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;ALTER TABLE employees ALTER COLUMN id SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN name SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN salary SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN birthdate SET TAGS ('sensitivity' = 'true');&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_1-1773281812230.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24737iBF86BEF5D3E6BA6E/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_1-1773281812230.png" alt="MoJaMa_1-1773281812230.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Write one VARIANT UDF that branches on &lt;/SPAN&gt;&lt;SPAN&gt;schema_of_variant()&lt;/SPAN&gt;&lt;SPAN&gt;:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE FUNCTION mask_sensitive(data VARIANT)
RETURNS VARIANT
RETURN CASE
  WHEN schema_of_variant(data) = 'BIGINT' THEN 0::BIGINT::VARIANT
  WHEN schema_of_variant(data) = 'DOUBLE' THEN 0.00::DOUBLE::VARIANT
  WHEN schema_of_variant(data) = 'DATE'   THEN DATE'1970-01-01'::VARIANT
  WHEN schema_of_variant(data) = 'STRING' THEN '***REDACTED***'::VARIANT
  ELSE NULL::VARIANT
END;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_2-1773281812230.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24738i311D19699FA22116/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_2-1773281812230.png" alt="MoJaMa_2-1773281812230.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Create one schema-level ABAC policy that auto-applies to every tagged column:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE POLICY mask_all_sensitive
ON SCHEMA my_catalog.my_schema
COLUMN MASK mask_sensitive
TO `account users`
FOR TABLES
MATCH COLUMNS hasTag('sensitivity') AS col
ON COLUMN col;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_3-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24739i1BFCFE71F439FBF9/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_3-1773281812231.png" alt="MoJaMa_3-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;That's it. Any column tagged &lt;/SPAN&gt;&lt;SPAN&gt;sensitivity = true&lt;/SPAN&gt;&lt;SPAN&gt; — regardless of type — is now masked for all users.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Insert Data&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;Sample data (4 rows):&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;TABLE&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;id&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;name&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;salary&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;birthdate&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;dept&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Alice Johnson&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;95000.50&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1985-03-12&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Engineering&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;2&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Bob Smith&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;120000.00&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1979-07-22&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Finance&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;3&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Carol White&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;78500.75&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1992-11-05&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Marketing&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;4&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;David Lee&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;145000.00&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1975-01-30&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Engineering&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;What the Results Look Like&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;As you can see all the columns, even though they are different data types, are masked now, except for the &lt;EM&gt;dept&lt;/EM&gt; column which we are just using as a control column for illustration purposes.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_4-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24741i19DEB9D25A6F47A4/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_4-1773281812231.png" alt="MoJaMa_4-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;And the column types stay native. ABAC automatically coerces the VARIANT return value back to the original column type — no extra JSON quotes, no broken &lt;/SPAN&gt;&lt;SPAN&gt;ORDER BY&lt;/SPAN&gt;&lt;SPAN&gt;, no explicit casts needed in queries.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;What the Results Look Like for Policy-Exempted Users&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;Sometimes you want a principal to be exempted from the ABAC Policy as they are allowed to see the unmasked/unfiltered data and/or want to enable them to Delta Share tables with ABAC RLS/CLM for which they need to be in the EXCEPT clause. You would add them as seen below:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_5-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24740i73EE194502F376F3/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_5-1773281812231.png" alt="MoJaMa_5-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Mohan Mathews (ie me) is part of the EXCEPT clause so is able to see the unmasked data.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_6-1773281812232.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24742i7CD508B8A92B49C2/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_6-1773281812232.png" alt="MoJaMa_6-1773281812232.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;A Few Gotchas Worth Knowing&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;`schema_of_variant()` maps INT to BIGINT.&lt;/STRONG&gt;&lt;SPAN&gt; When an INT is cast to VARIANT, &lt;/SPAN&gt;&lt;SPAN&gt;schema_of_variant()&lt;/SPAN&gt;&lt;SPAN&gt; returns &lt;/SPAN&gt;&lt;SPAN&gt;'BIGINT'&lt;/SPAN&gt;&lt;SPAN&gt;, not &lt;/SPAN&gt;&lt;SPAN&gt;'INT'&lt;/SPAN&gt;&lt;SPAN&gt;. If your CASE checks for &lt;/SPAN&gt;&lt;SPAN&gt;'INT'&lt;/SPAN&gt;&lt;SPAN&gt; it will silently fall through to &lt;/SPAN&gt;&lt;SPAN&gt;NULL&lt;/SPAN&gt;&lt;SPAN&gt;. Always use &lt;/SPAN&gt;&lt;SPAN&gt;'BIGINT'&lt;/SPAN&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;ABAC type coercion doesn't happen with traditional column masks.&lt;/STRONG&gt;&lt;SPAN&gt; If you apply a VARIANT-returning UDF as a regular &lt;/SPAN&gt;&lt;SPAN&gt;ALTER TABLE SET MASK&lt;/SPAN&gt;&lt;SPAN&gt;, the columns come back as VARIANT type in query results — STRING/DATE values get JSON quotes, and &lt;/SPAN&gt;&lt;SPAN&gt;ORDER BY&lt;/SPAN&gt;&lt;SPAN&gt; on integer columns breaks. ABAC was specifically designed to handle VARIANT with automatic type casting. Stick with ABAC for this pattern.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Governed tags require account admin to create.&lt;/STRONG&gt;&lt;SPAN&gt; The &lt;/SPAN&gt;&lt;SPAN&gt;hasTag()&lt;/SPAN&gt;&lt;SPAN&gt; condition in ABAC policies only works with governed tags (not regular UC object tags). These are created once by an account admin via &lt;/SPAN&gt;&lt;STRONG&gt;Catalog &amp;gt; Govern &amp;gt; Governed Tags&lt;/STRONG&gt;&lt;SPAN&gt; in the workspace UI. Once created and assigned, any user with table ownership can tag columns and the policy auto-applies. Also: create your tag with at least one allowed value — an empty allowed values list will reject all tag assignments.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Why This Matters&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;The traditional approach — one mask function per column type, applied per column — doesn't scale. With ABAC + VARIANT:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;One function&lt;/STRONG&gt;&lt;SPAN&gt; handles any column type you throw at it&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;One policy&lt;/STRONG&gt;&lt;SPAN&gt; at the schema level auto-applies as new tagged tables are added&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;Zero per-column management&lt;/STRONG&gt;&lt;SPAN&gt; after initial setup&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;Native types preserved&lt;/STRONG&gt;&lt;SPAN&gt; in query results with no workarounds&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;It's a clean, scalable pattern for organizations with large data estates and mixed-type PII across many tables.&lt;/SPAN&gt;&lt;/P&gt;</description>
    <pubDate>Thu, 12 Mar 2026 02:26:54 GMT</pubDate>
    <dc:creator>MoJaMa</dc:creator>
    <dc:date>2026-03-12T02:26:54Z</dc:date>
    <item>
      <title>One Policy to Mask Them All: ABAC + VARIANT in Unity Catalog</title>
      <link>https://community.databricks.com/t5/community-articles/one-policy-to-mask-them-all-abac-variant-in-unity-catalog/m-p/150648#M1066</link>
      <description>&lt;P&gt;&lt;I&gt;&lt;SPAN&gt;Databricks ABAC lets you apply a single schema-level policy across columns of any data type — no more managing one mask function per type. Here's ho&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;w to use the VARIANT data type to make it work.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;If you've implemented column masking in Unity Catalog, you've probably run into this friction: every column type needs its own mask function. A STRING column needs a STRING-returning UDF. A DATE column needs a DATE-returning UDF. Multiply that across a schema with dozens of sensitive columns and you're managing a small army of functions and policies.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;There's a better way. By combining &lt;/SPAN&gt;&lt;STRONG&gt;ABAC (Attribute-Based Access Control)&lt;/STRONG&gt;&lt;SPAN&gt; with the &lt;/SPAN&gt;&lt;STRONG&gt;VARIANT data type&lt;/STRONG&gt;&lt;SPAN&gt;, you can write one function and one policy that handles every column type automatically.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;The Setup&lt;/SPAN&gt;&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Create a test table. In this example it’s the employees table.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE TABLE abac_variant_test_catalog.abac_test.employees (
  id INT,
  name STRING,
  salary DOUBLE,
  birthdate DATE,
  dept STRING -- control column, not masked)
COMMENT 'The table contains records of employees, including relevant details such as their names, salaries, birthdates, and departments. Possible use cases include analyzing salary distributions, tracking employee demographics, and evaluating departmental staffing levels.'&lt;/LI-CODE&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Tag your sensitive columns with a governed tag — no matter what type they are:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_0-1773281812229.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24736i7A3BDA1B26CE808A/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_0-1773281812229.png" alt="MoJaMa_0-1773281812229.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;ALTER TABLE employees ALTER COLUMN id SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN name SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN salary SET TAGS ('sensitivity' = 'true');
ALTER TABLE employees ALTER COLUMN birthdate SET TAGS ('sensitivity' = 'true');&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_1-1773281812230.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24737iBF86BEF5D3E6BA6E/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_1-1773281812230.png" alt="MoJaMa_1-1773281812230.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Write one VARIANT UDF that branches on &lt;/SPAN&gt;&lt;SPAN&gt;schema_of_variant()&lt;/SPAN&gt;&lt;SPAN&gt;:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE FUNCTION mask_sensitive(data VARIANT)
RETURNS VARIANT
RETURN CASE
  WHEN schema_of_variant(data) = 'BIGINT' THEN 0::BIGINT::VARIANT
  WHEN schema_of_variant(data) = 'DOUBLE' THEN 0.00::DOUBLE::VARIANT
  WHEN schema_of_variant(data) = 'DATE'   THEN DATE'1970-01-01'::VARIANT
  WHEN schema_of_variant(data) = 'STRING' THEN '***REDACTED***'::VARIANT
  ELSE NULL::VARIANT
END;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_2-1773281812230.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24738i311D19699FA22116/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_2-1773281812230.png" alt="MoJaMa_2-1773281812230.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN&gt;Create one schema-level ABAC policy that auto-applies to every tagged column:&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;LI-CODE lang="python"&gt;CREATE POLICY mask_all_sensitive
ON SCHEMA my_catalog.my_schema
COLUMN MASK mask_sensitive
TO `account users`
FOR TABLES
MATCH COLUMNS hasTag('sensitivity') AS col
ON COLUMN col;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_3-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24739i1BFCFE71F439FBF9/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_3-1773281812231.png" alt="MoJaMa_3-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;That's it. Any column tagged &lt;/SPAN&gt;&lt;SPAN&gt;sensitivity = true&lt;/SPAN&gt;&lt;SPAN&gt; — regardless of type — is now masked for all users.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Insert Data&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;Sample data (4 rows):&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;TABLE&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;id&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;name&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;salary&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;birthdate&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;dept&lt;/STRONG&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Alice Johnson&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;95000.50&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1985-03-12&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Engineering&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;2&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Bob Smith&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;120000.00&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1979-07-22&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Finance&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;3&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Carol White&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;78500.75&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1992-11-05&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Marketing&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;4&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;David Lee&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;145000.00&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;1975-01-30&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;TD&gt;
&lt;P&gt;&lt;SPAN&gt;Engineering&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;What the Results Look Like&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;As you can see all the columns, even though they are different data types, are masked now, except for the &lt;EM&gt;dept&lt;/EM&gt; column which we are just using as a control column for illustration purposes.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_4-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24741i19DEB9D25A6F47A4/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_4-1773281812231.png" alt="MoJaMa_4-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;And the column types stay native. ABAC automatically coerces the VARIANT return value back to the original column type — no extra JSON quotes, no broken &lt;/SPAN&gt;&lt;SPAN&gt;ORDER BY&lt;/SPAN&gt;&lt;SPAN&gt;, no explicit casts needed in queries.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;What the Results Look Like for Policy-Exempted Users&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;Sometimes you want a principal to be exempted from the ABAC Policy as they are allowed to see the unmasked/unfiltered data and/or want to enable them to Delta Share tables with ABAC RLS/CLM for which they need to be in the EXCEPT clause. You would add them as seen below:&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_5-1773281812231.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24740i73EE194502F376F3/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_5-1773281812231.png" alt="MoJaMa_5-1773281812231.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Mohan Mathews (ie me) is part of the EXCEPT clause so is able to see the unmasked data.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="MoJaMa_6-1773281812232.png" style="width: 400px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/24742i7CD508B8A92B49C2/image-size/medium?v=v2&amp;amp;px=400" role="button" title="MoJaMa_6-1773281812232.png" alt="MoJaMa_6-1773281812232.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;A Few Gotchas Worth Knowing&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;`schema_of_variant()` maps INT to BIGINT.&lt;/STRONG&gt;&lt;SPAN&gt; When an INT is cast to VARIANT, &lt;/SPAN&gt;&lt;SPAN&gt;schema_of_variant()&lt;/SPAN&gt;&lt;SPAN&gt; returns &lt;/SPAN&gt;&lt;SPAN&gt;'BIGINT'&lt;/SPAN&gt;&lt;SPAN&gt;, not &lt;/SPAN&gt;&lt;SPAN&gt;'INT'&lt;/SPAN&gt;&lt;SPAN&gt;. If your CASE checks for &lt;/SPAN&gt;&lt;SPAN&gt;'INT'&lt;/SPAN&gt;&lt;SPAN&gt; it will silently fall through to &lt;/SPAN&gt;&lt;SPAN&gt;NULL&lt;/SPAN&gt;&lt;SPAN&gt;. Always use &lt;/SPAN&gt;&lt;SPAN&gt;'BIGINT'&lt;/SPAN&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;ABAC type coercion doesn't happen with traditional column masks.&lt;/STRONG&gt;&lt;SPAN&gt; If you apply a VARIANT-returning UDF as a regular &lt;/SPAN&gt;&lt;SPAN&gt;ALTER TABLE SET MASK&lt;/SPAN&gt;&lt;SPAN&gt;, the columns come back as VARIANT type in query results — STRING/DATE values get JSON quotes, and &lt;/SPAN&gt;&lt;SPAN&gt;ORDER BY&lt;/SPAN&gt;&lt;SPAN&gt; on integer columns breaks. ABAC was specifically designed to handle VARIANT with automatic type casting. Stick with ABAC for this pattern.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Governed tags require account admin to create.&lt;/STRONG&gt;&lt;SPAN&gt; The &lt;/SPAN&gt;&lt;SPAN&gt;hasTag()&lt;/SPAN&gt;&lt;SPAN&gt; condition in ABAC policies only works with governed tags (not regular UC object tags). These are created once by an account admin via &lt;/SPAN&gt;&lt;STRONG&gt;Catalog &amp;gt; Govern &amp;gt; Governed Tags&lt;/STRONG&gt;&lt;SPAN&gt; in the workspace UI. Once created and assigned, any user with table ownership can tag columns and the policy auto-applies. Also: create your tag with at least one allowed value — an empty allowed values list will reject all tag assignments.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;Why This Matters&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;The traditional approach — one mask function per column type, applied per column — doesn't scale. With ABAC + VARIANT:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;One function&lt;/STRONG&gt;&lt;SPAN&gt; handles any column type you throw at it&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;One policy&lt;/STRONG&gt;&lt;SPAN&gt; at the schema level auto-applies as new tagged tables are added&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;Zero per-column management&lt;/STRONG&gt;&lt;SPAN&gt; after initial setup&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;Native types preserved&lt;/STRONG&gt;&lt;SPAN&gt; in query results with no workarounds&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;It's a clean, scalable pattern for organizations with large data estates and mixed-type PII across many tables.&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Thu, 12 Mar 2026 02:26:54 GMT</pubDate>
      <guid>https://community.databricks.com/t5/community-articles/one-policy-to-mask-them-all-abac-variant-in-unity-catalog/m-p/150648#M1066</guid>
      <dc:creator>MoJaMa</dc:creator>
      <dc:date>2026-03-12T02:26:54Z</dc:date>
    </item>
  </channel>
</rss>

