Snowflake masking policies let you dynamically hide or obfuscate sensitive column data based on the querying user’s role.
Data masking hides sensitive values—like emails or card numbers—so analysts can query tables without seeing private information. Policies return either the real value or a masked version depending on the user’s role.
A masking policy is a schema object. You define an input parameter, return type, and a CASE expression that checks CURRENT_ROLE(). You then bind the policy to one or more table columns.
CREATE OR REPLACE MASKING POLICY policy_name
AS (val <data_type>)
RETURNS <data_type>
-> CASE WHEN <role condition> THEN val ELSE <masked value> END;
Identify the sensitive column, decide which roles see the clear value, and build a simple CASE expression. Keep the logic short for performance.
CREATE OR REPLACE MASKING POLICY mask_customer_email
AS (email STRING)
RETURNS STRING
-> CASE
WHEN CURRENT_ROLE() IN ('ACCOUNTADMIN','SECURITYADMIN') THEN email
ELSE regexp_replace(email,'[^@]+','***')
END;
Use ALTER TABLE … MODIFY COLUMN to bind the policy. Queries start honoring the rule instantly; no data rewrite occurs.
ALTER TABLE Customers
MODIFY COLUMN email SET MASKING POLICY mask_customer_email;
Yes. Masking is enforced at query time, so the policy still runs when columns are joined, selected into CTEs, or materialized into views.
Store policies in a dedicated security schema, version them in source control, test with different roles, and avoid complex UDFs inside the CASE expression.
Forgetting to grant roles access to the policy’s schema causes “policy does not exist” errors. Also, applying multiple policies on the same column via tags overrides earlier bindings.
Masking policies give fine-grained, role-based data protection without copying tables. Create the policy once, bind it to sensitive columns, and audit regularly.
Yes. Use string functions inside the CASE expression to concat a static mask with the visible portion of the value.
Policies add minimal overhead because they run as simple CASE expressions during query compilation, not row by row at runtime.
Use ACCESS_HISTORY and ACCOUNT_USAGE views to track which roles ran queries returning clear values.