3.4 Common Traps in Account Management and Data Governance
Key Takeaways
- Column-level security uses masking policies (dynamic data masking and external tokenization) to hide or transform column values based on the querying role.
- Row access policies filter which rows a role sees; a single policy can be attached to many tables and uses a mapping/condition.
- Object tagging applies key-value tags to objects; tag-based masking policies let one policy protect every column carrying a given tag.
- Secure views (CREATE SECURE VIEW) hide the underlying query definition and prevent optimizer-based data leakage, unlike regular views.
- ACCESS_HISTORY and OBJECT_DEPENDENCIES (in ACCOUNT_USAGE) support auditing of who read what and how objects relate; classification flags PII columns.
Column-Level Security: Masking Policies
Dynamic Data Masking uses a masking policy — a schema-level object — to transform a column's values at query time based on the current role (or other context). The underlying data is never altered; the policy rewrites what is returned. External tokenization is the related feature that tokenizes data before loading and detokenizes via a policy.
CREATE MASKING POLICY mask_ssn AS (val string) RETURNS string ->
CASE WHEN CURRENT_ROLE() IN ('AUDITOR') THEN val
ELSE '***-**-****' END;
ALTER TABLE customers MODIFY COLUMN ssn SET MASKING POLICY mask_ssn;
Trap: masking does not stop a role from querying the table — the role still gets rows back, just with masked column values. Candidates wrongly assume a masking policy is an access denial. It is a transformation. Another trap: masking policies are column-level; to filter rows, you need a different feature.
Row Access Policies, Tags, and Classification
A row access policy controls which rows a role can see. The policy returns a boolean per row; rows that evaluate false are filtered out. A single policy can be attached to many tables and often references a mapping table of role-to-data entitlements.
CREATE ROW ACCESS POLICY region_filter AS (region string) RETURNS boolean ->
EXISTS (SELECT 1 FROM role_region_map m
WHERE m.role = CURRENT_ROLE() AND m.region = region);
Object tagging assigns key-value tags (e.g., pii = 'true') to objects. The power feature is the tag-based masking policy: assign a masking policy to a tag, and every column carrying that tag is automatically protected — no need to alter each column. This is heavily tested as the efficient way to mask "all PII columns."
Data classification (SYSTEM$CLASSIFY / the classification feature) scans columns and proposes semantic and privacy categories (e.g., that a column holds an email or a national ID), which you can then tag. It helps you find sensitive data so governance policies can be applied.
| Need | Feature |
|---|---|
| Hide/transform a column's values | Masking policy |
| Show only some rows to a role | Row access policy |
| Label objects/columns | Object tags |
| Protect every column with a tag at once | Tag-based masking policy |
| Discover PII automatically | Data classification |
Secure Views and Auditing
A secure view (CREATE SECURE VIEW) differs from a regular view in two ways the exam loves: (1) its definition is hidden from users who do not own it, and (2) the optimizer is restricted so it cannot leak data from the underlying tables through clever predicates or error messages. The trade-off is that secure views can be slower because some optimizations are disabled. Use a secure view (or secure UDF) when sharing data or exposing a filtered slice; secure views are required for sharing data in some scenarios.
Auditing with ACCOUNT_USAGE
The SNOWFLAKE.ACCOUNT_USAGE schema provides governance audit views:
- ACCESS_HISTORY — records who accessed what: which queries read or wrote which columns/objects, supporting data-access auditing and compliance.
- OBJECT_DEPENDENCIES — shows how objects reference each other (e.g., which views depend on which tables), useful for impact analysis before changes.
Trap: ACCOUNT_USAGE views can have up to ~45 minutes to 3 hours of latency and retain history (typically 365 days), whereas the INFORMATION_SCHEMA is real-time but limited to the current database and shorter history. If a stem needs cross-account, historical, account-wide auditing, choose ACCOUNT_USAGE; if it needs live metadata about the current database, choose INFORMATION_SCHEMA. Confusing the two is a classic miss.
Putting the Governance Features Together
Real governance designs layer these features, and the exam rewards knowing the order they apply. When a role queries a table: first RBAC decides whether the role can reach the object at all; then any row access policy filters the visible rows; then any masking policy (column-level or tag-based) transforms the surviving column values. A role can therefore hold SELECT, pass the row policy, and still see masked columns — three independent gates.
Policy management notes
- Masking and row access policies are schema-level securable objects; creating one needs CREATE MASKING POLICY / CREATE ROW ACCESS POLICY on the schema, and applying one needs APPLY on the policy (or ownership of the object).
- A single policy is reusable: attach one masking policy to many columns, or one row access policy to many tables, for consistent enforcement.
- Policies reference context functions such as
CURRENT_ROLE(),CURRENT_USER(), andINVOKER_ROLE()to decide what to reveal.
Trap roundup
| Misconception | Reality |
|---|---|
| Masking blocks access | It transforms returned values; the role still queries |
| One masking policy can hide rows | Masking is column-level; use a row access policy for rows |
| Regular views hide their definition | Only secure views hide the definition and stop leakage |
| INFORMATION_SCHEMA gives long account-wide history | That is ACCOUNT_USAGE; INFORMATION_SCHEMA is real-time, current-database, short history |
| Tagging by itself protects data | A tag only labels; pair it with a tag-based masking policy to enforce |
Memorizing this table converts the most common governance distractors into easy points, because each exam trap maps directly to one row.
An auditor needs the most efficient way to mask every column in the account that has been tagged as PII. What should they use?
Which feature filters which ROWS a role can see from a table, rather than transforming column values?
A compliance team needs account-wide history of which users read which columns over the past several months. Which source should they query?
What is a key advantage of a secure view over a regular view?