What you will be able to do
- Explain how Dynamic Data Masking resolves at query time, and why the same feature column can look different to training and inference queries
- Separate policy ownership from policy application so table owners cannot remove protections
- Build a row access policy with a mapping table and attach it to a table
- Govern models and features at scale with tag-based masking, tag-controlled model promotion and ML Lineage
1.Dynamic Data Masking at training and inference time
Dynamic Data Masking is Snowflake's column-level security feature. A masking policy is a schema-level object that masks plain-text data in table and view columns when a query runs. The table itself is never rewritten. You can write one policy and apply it to thousands of columns across databases and schemas.
What a query sees depends on three things: the policy's conditions, the SQL execution context, and the role hierarchy. Depending on those, the query sees the plain-text value, a partially masked value or a fully masked value. For ML this matters a lot. Masking is resolved separately for each query, based on who is running it. So a training pipeline running under one role and a scoring query running under another can receive different values from the same feature column. A model trained on clear values and then fed masked values at inference is getting different data from what it learned on. The fix is in the policy conditions and in which role runs the training and scoring queries, not in the model. A policy can even be written so that ACCOUNTADMIN or SECURITYADMIN cannot see the raw data unless there is a reason to.
A few mechanical rules come up often. A column can be attached to only one masking policy. A policy's return type must match its input type, or creating the policy fails with an argument and return type mismatch. Policies cannot be attached to virtual columns, and masked columns cannot be used inside materialized views. For auditing, the POLICY_REFERENCES view lists every object where a masking policy is set. The Query Profile shows which policies a particular query used. The Account Usage QUERY_HISTORY view does not include policy names.
Checkpoint 1 of 7· Check yourself
An auditor wants to know which masking policies were applied to one particular feature-generation query. Where should they look?
QUERY_HISTORY stores the SQL text but not policy names. The Query Profile shows the masking policies a specific query used.
“The masking policy names that were used in a specific query can be found in the Query Profile.”Source: docs.snowflake.com
Checkpoint 2 of 7· Exam question
A producer role registers a feature view with incremental refresh over a source table owned by another team's role. Registration fails because change tracking is not enabled on the source table. What resolves this while keeping ownership with the other team?
Correct answer: B — Have the source table's owner role run ALTER TABLE ... SET CHANGE_TRACKING = TRUE, or let the producer role hold OWNERSHIP to enable it automatically.
- A. Incorrect. OPERATE controls suspending and resuming refresh, but incremental refresh still needs change tracking on the source table.
- B. Correct. Incremental refresh depends on change tracking; the table owner can enable it, or the producer needs OWNERSHIP so the Feature Store can enable it.
- C. Incorrect. A grant option lets a role pass on SELECT, but it has no effect on whether change tracking is enabled for the table.
- D. Incorrect. DOWNSTREAM only sets the refresh timing relative to dependents and never derives change data from query history.
Sources1
2.Who owns a policy and who can apply it
Masking exists to support separation of duties. A security or privacy officer decides which columns are protected, not the owner of the table. Snowflake enforces this with two separate privileges on the policy object. Operating on a policy also needs USAGE on its parent database and schema.
| Privilege | Grants | Typical holder |
|---|---|---|
| CREATE MASKING POLICY (on account) | Create or replace masking policies | Central security role |
| OWNERSHIP | Full control; required to alter most policy properties; held by one role at a time | Central security role |
| APPLY | Set and unset the policy on a column | Only the roles allowed to attach or remove protection |
| APPLY MASKING POLICY (on account) | Global apply; also allows DESCRIBE on tables and views | A small number of governance administrators |
Owning a table does not include APPLY. The documented error cases show this: the role that owns a cloned table cannot unset that table's masking policy until it is granted APPLY on the policy. So if a central team owns the policies and keeps APPLY to itself, ML teams that own the feature tables cannot unset or swap the protection. Row access policies use the same model. In the documented example, SECURITYADMIN hands OWNERSHIP of a row access policy to a custom role and grants APPLY to an analyst role. Policies run with the owner's rights, so the less privileged custom role's privileges are used at runtime, not SECURITYADMIN's.
Checkpoint 3 of 7· Check yourself
A central security team must manage masking policies for every ML schema. The ML teams own the feature tables and must not be able to remove those policies. Which design meets this?
Setting and unsetting a policy needs APPLY, and table ownership does not include it. Keeping OWNERSHIP and APPLY with the central role stops table owners from changing the protection.
“Enables executing the unset and set operations for a masking policy on a column.”Source: docs.snowflake.com
3.Row access policies for training and scoring data
Masking controls which values in a column you see. A row access policy controls which rows you see. The policy has a signature (the columns and data types it takes) and a body that returns BOOLEAN. The result decides whether the user can see a given row in the table or view the policy is attached to. A common pattern looks up a mapping table: for example, one role sees every row, while other roles see only the regions listed for them in the mapping table. The schema owner role automatically gets CREATE ROW ACCESS POLICY and can grant it to other roles.
Pick the context function carefully. CURRENT_ROLE matches only the active role. If role activation and the role hierarchy matter, Snowflake recommends IS_ROLE_IN_SESSION for account roles and IS_DATABASE_ROLE_IN_SESSION for database roles. A mapping table can itself be protected by a row access policy. If the mapping-table lookups slow queries down, replace each EXISTS subquery with a memoizable function. You can attach a policy to an existing table with ALTER TABLE … ADD ROW ACCESS POLICY … ON (column), or when you create the table if the policy already exists.
CREATE OR REPLACE ROW ACCESS POLICY governance.policies.rap_map_exempt AS (allowed_roles varchar) RETURNS BOOLEAN -> IS_ROLE_IN_SESSION(allowed_roles);Checkpoint 4 of 7· Put it in order
Put the documented mapping-table setup in order.
- 1.Grant OWNERSHIP of the policy to the mapping role and APPLY to the analyst role
- 2.Create a mapping role and grant it SELECT on the mapping table
- 3.Create the table to be protected and the mapping table in the security schema
- 4.Using the schema owner role, create the row access policy
- 5.Add the policy to the region column with ALTER TABLE, then grant SELECT and test
The roles and the mapping table must exist before the policy can refer to them. Ownership is moved off SECURITYADMIN before the policy is bound to the table and tested.
“Add (bind) the row access policy to the region column in the sales data table”Source: docs.snowflake.com
Sources2
4.Governance across feature stores and model registries
Attaching policies column by column doesn't scale when feature source tables keep gaining columns. Tag-based masking solves this. You create a tag, create a masking policy, attach the policy to the tag with ALTER TAG, and then set the tag on a table, schema or database. A tag can carry only one masking policy per data type. If a column has a directly assigned policy and also gets one through a tag, the directly assigned policy wins.
Checkpoint 5 of 7· Check yourself
A feature column has masking policy A assigned directly. Its schema also carries a tag that has masking policy B. Which policy masks the column?
When a column has both a direct policy and a tag-based policy, the direct assignment takes precedence.
“the directly assigned masking policy takes precedence over the masking policy assigned to the tag.”Source: docs.snowflake.com
For models, the governance question is who is allowed to promote a version to production. The registry supports four schemes, and they differ in where that authority sits. Tags are securable through RBAC, so a production role can hold the tag that names the live version while data scientists keep ownership of the model. For that role to see and read the tags, it needs APPLY TAG on the account and USAGE on the model's schema.
| Scheme | Who promotes | Mechanism |
|---|---|---|
| Default version | Model owner | ALTER MODEL my_model SET DEFAULT_VERSION |
| Aliases | Model owner | ALTER MODEL my_model VERSION v2 SET ALIAS = production |
| Tags | A role other than the owner | SET TAG live_version on the model; read it with SYSTEM$GET_TAG (domain MODULE) |
| Multiple schemas | A role other than the owner | Copy the version into a production schema; the promoter needs OWNERSHIP or READ on the source model |
USE ROLE prod_role;
ALTER MODEL model_database.model_schema.my_model
SET TAG prod_db.prod_schema.live_version = 'V2';Compliance also means being able to show what a model was built from. ML Lineage traces source tables to feature views, datasets, models and deployed services, so it answers questions like where a model's training data came from. Lineage for a model is recorded when the model is logged to the registry. Exploring lineage from the Python APIs needs the VIEW LINEAGE privilege. ACCOUNTADMIN has it by default and can grant it at account level. Lineage has limits: it is not replicated, and tables built from model predictions do not link back to the model. Metadata such as accuracy metrics can be queried across all models through INFORMATION_SCHEMA.MODEL_VERSIONS.
Checkpoint 6 of 7· Match them up
Match each governance need to the control that meets it.
Tap a term, then the definition that fits it.
Tags separate promotion authority from ownership. A tag carrying a masking policy protects data across a whole schema. VIEW LINEAGE is required to explore lineage from the Python APIs.
“Users need the VIEW LINEAGE privilege to explore lineage from Python APIs.”Source: docs.snowflake.com
Checkpoint 7 of 7· Exam question
A governance engineer wants masking logic that depends on another column, such as showing an email only when a consent flag column is TRUE. Which statement describes how to implement this?
Correct answer: A — Create a conditional masking policy that takes the consent column as an extra argument, where the referenced columns must be in the same table or view.
- A. Correct. Conditional masking policies accept additional columns as arguments, and those columns must reside in the same table or view as the masked column.
- B. Incorrect. Cross-table lookups are not how conditional masking passes the extra column; the signature has to name columns of the protected object.
- C. Incorrect. A column can carry only one masking policy at a time, so two policies cannot be stacked and selected by value.
- D. Incorrect. Row access policies filter rows rather than transform a column value, and masking policies can use additional columns via conditional masking.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.The role that owns a feature table can unset or replace the masking policy on its columns.Why is that wrong?
Setting and unsetting a policy on a column needs APPLY on the policy. Table ownership does not include APPLY, so a central team that keeps APPLY keeps control.
Covered in Who owns a policy and who can apply it
2.A tag-based masking policy on a schema overrides any masking policy assigned directly to a column.Why is that wrong?
It is the other way round. When both exist on a column, the directly assigned policy takes precedence over the policy that comes from the tag.
Covered in Governance across feature stores and model registries
3.A row access policy written with CURRENT_ROLE() automatically respects the role hierarchy.Why is that wrong?
CURRENT_ROLE matches only the active role. When the role hierarchy matters, Snowflake recommends IS_ROLE_IN_SESSION for account roles.
Covered in Row access policies for training and scoring data
Practise it for real
Protect a table with a row access policy that is owned by a custom role, not by SECURITYADMIN
1.Create the sales table and the security.salesmanagerregions mapping table.
Why: The policy body looks up the mapping table to decide which regions each manager role can see.
You should see: Both tables exist and are empty.
2.As SECURITYADMIN, create mapping_role and grant it SELECT on security.salesmanagerregions.
Why: The policy runs with its owner's rights, so the owning role must be able to read the mapping table.
You should see: SHOW GRANTS TO ROLE mapping_role lists SELECT on the mapping table.
3.As the schema owner role, create security.sales_policy with the CURRENT_ROLE and EXISTS conditions.
Why: The schema owner role is automatically granted CREATE ROW ACCESS POLICY.
You should see: SHOW ROW ACCESS POLICIES shows security.sales_policy.
4.Grant OWNERSHIP of the policy to mapping_role and APPLY to sales_analyst_role, then run ALTER TABLE sales ADD ROW ACCESS POLICY security.sales_policy ON (region).
Why: Moving ownership off SECURITYADMIN follows least privilege, and APPLY lets the analyst role add or drop the policy.
You should see: The policy is bound to the region column.
5.Grant SELECT on sales to sales_manager_role, switch to that role and run the aggregate query.
Why: This confirms the policy filters rows for a non-executive role.
You should see: Only rows for regions mapped to that manager are returned.
Stuck? Get a nudge
If the manager sees no rows, check the mapping table: the policy compares sales_manager to CURRENT_ROLE(), so the stored value must match the role name exactly.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“uses masking policies to selectively mask plain-text data in table and view columns at query time”
↩︎ Dynamic Data Masking at training and inference time“Snowflake query operators may see the plain-text value, a partially masked value, or a fully masked value.”
↩︎ Dynamic Data Masking at training and inference time“A column cannot be attached to multiple masking policies.”
↩︎ Dynamic Data Masking at training and inference time“A security or privacy officer decides which columns to protect, not the object owner.”
↩︎ Who owns a policy and who can apply it“Enables executing the unset and set operations for a masking policy on a column.”
↩︎ Exam trap 1“At query runtime, the masking policy is applied to the column at every location where the column appears.”
↩︎ Prediction“The masking policy names that were used in a specific query can be found in the Query Profile.”
↩︎ Checkpoint“Enables executing the unset and set operations for a masking policy on a column.”
↩︎ Checkpoint - 2.
“policies are executed with owner’s rights, not the more privileged SECURITYADMIN system role.”
↩︎ Who owns a policy and who can apply it“The returned value determines whether the user has access to a given row on the table or view”
↩︎ Row access policies for training and scoring data“the schema owner role is automatically granted the CREATE ROW ACCESS POLICY privilege.”
↩︎ Row access policies for training and scoring data“Snowflake recommends that the policy conditions use the IS_ROLE_IN_SESSION function for account roles”
↩︎ Exam trap 3“Add (bind) the row access policy to the region column in the sales data table”
↩︎ Checkpoint - 3.
“A tag can have only one masking policy per data type.”
↩︎ Governance across feature stores and model registries“the directly assigned masking policy takes precedence over the masking policy assigned to the tag.”
↩︎ Exam trap 2“the directly assigned masking policy takes precedence over the masking policy assigned to the tag.”
↩︎ Checkpoint - 4.https://docs.snowflake.com/en/developer-guide/snowflake-ml/model-registry/model-managementOfficial docs
“Tags are securable by role-based access control and are suitable for this separation of responsibility.”
↩︎ Governance across feature stores and model registries - 5.
“Lineage for models is captured when the model is logged to the Model Registry.”
↩︎ Governance across feature stores and model registries“Users need the VIEW LINEAGE privilege to explore lineage from Python APIs.”
↩︎ Checkpoint