> Quantumizing
← All 50 Days
Day 18 of 50
D2: Account & Governance Week 3
DAY 18

RBAC + DAC in Snowflake

Domain 2 starts here, carrying 20% of the COF-C03 exam across nine days, and Day 18 lays the foundation. The two access-control models Snowflake runs side by side are RBAC (privileges go to roles, roles go to users) and DAC (each object has an owner role that grants access on it), with the official C03 sample exam Q4 wired directly to DAC.

🗣️ Plain-English First
TermPlain meaning
Access controlThe rules that decide who can do what to which objects. Snowflake combines DAC + RBAC (and optionally UBAC).
Securable objectAnything you can grant or revoke privileges on: database, schema, table, warehouse, role. Access is denied by default. Nothing is implicit.
RoleA named bundle of privileges. Roles are granted to users (and to other roles). In Snowflake, the unit of permission is the role, not the user.
PrivilegeA specific permitted action on an object. SELECT on a table, USAGE on a schema, OPERATE on a warehouse.
OwnerThe role that holds the OWNERSHIP privilege on an object. Roles own objects. Users do not.
RBACRole-Based Access Control. Privileges → Roles → Users. The model Snowflake uses for scalable production access.
DACDiscretionary Access Control. The object’s owner role can grant access on it at its own discretion. Runs alongside RBAC.
UBACUser-Based Access Control. Privileges granted directly to a user. Effective only when USE SECONDARY ROLES = ALL. Narrow use case.
📘

Today’s Concept

Micro-Concept 1: The Two Sentences That Carry Half the Domain

Two sentences answer most Domain 2 access-control questions on their own.

RBAC: “Privileges are granted to roles, and roles are granted to users.”
DAC: “Each object has an owner role that can grant access on it.”

Snowflake runs both at the same time. They are not alternatives. RBAC is the assignment model. DAC is the ownership model. Every object you create has an owner role (DAC), and every privilege on that object is delivered through a role grant (RBAC).

Sample Q4 from the official C03 study guide reads, paraphrased: “Which Snowflake access control framework allows the owner of an object to grant access to other users?” The answer is Discretionary Access Control (DAC). The cue word is “owner.” If the stem says “owner grants access,” the answer is DAC. If the stem says “privileges assigned to groups of users,” the answer is RBAC. Read for the cue, then commit.

Micro-Concept 2: The Two-Step Privilege Flow (RBAC)

RBAC always flows in exactly two steps. Multi-select questions love to slip a third step into the options or merge the two into a single “grant directly to user” line. Both are wrong.

StepStatementExample SQL
1Grant the privilege to a role.GRANT SELECT ON TABLE sales.orders TO ROLE analyst_r;
2Grant the role to a user (or to another role).GRANT ROLE analyst_r TO USER alice;

Privileges are never granted directly to users in RBAC. That pattern is UBAC, which is a separate model with narrow scope (covered below). An exam option that says “in RBAC, privileges are granted to users” is a guaranteed wrong answer.

Roles can also be granted to other roles. That is how the role hierarchy works:

SQL
GRANT ROLE analyst_r TO ROLE SYSADMIN;
-- Anyone using SYSADMIN now also has every privilege analyst_r holds.

This is privilege inheritance. A parent role inherits every privilege of every child role granted to it. That is why ACCOUNTADMIN, sitting at the top of the system-role chain (ACCOUNTADMIN → SECURITYADMIN & SYSADMIN → USERADMIN → PUBLIC), can see and operate on everything inside its inheritance tree. The chain matters. We cover the system roles in detail on Day 19.

Micro-Concept 3: DAC, Ownership Belongs to a Role

The single most-missed point on DAC: roles own objects, not users. The role that is active in the session when CREATE runs becomes the owner. That ownership is the OWNERSHIP privilege, and it can be transferred to another role with a single grant.

SQL
USE ROLE analyst_r;
CREATE TABLE my_data (id INT);
-- analyst_r now owns my_data. analyst_r can grant SELECT on it to other roles.

-- Transfer ownership to a different role:
GRANT OWNERSHIP ON TABLE my_data TO ROLE data_steward_r;
-- analyst_r loses ownership; data_steward_r gains it.

Three consequences fall out of this rule, and exam stems exploit each of them:

  • If Alice and Bob both have analyst_r granted to them, they share ownership of anything analyst_r creates. Ownership belongs to the role; the role belongs to both of them.
  • If Alice leaves and her account is dropped, the table she created is not orphaned. analyst_r still owns it. Anyone else holding analyst_r can still manage it.
  • The owner role can grant any privilege on its object to any other role without involving ACCOUNTADMIN. That is the “discretionary” in DAC. The owner role decides on its own.

On a recent client migration, this is exactly why we never bind ownership to a personal user. Ownership tracks a service role. People rotate off projects; roles persist.

Micro-Concept 4: The Securable Object Hierarchy

Privileges are not standalone. They flow through a hierarchy of containers. Reading a table needs three privileges, not one.

Container levelPrivilege neededWhy
DatabaseUSAGEWithout it, the database is invisible.
SchemaUSAGEWithout it, you see the database but none of its contents.
TableSELECTThe read permission itself. Useless without USAGE on the levels above.

The required order is Database USAGE → Schema USAGE → Table SELECT. Outermost first, innermost last. Exam multi-selects will offer combinations like “schema SELECT” or “table USAGE.” Both are nonsense (tables do not have USAGE, schemas do not have SELECT), but they trip people up when the options are listed quickly.

The grant pattern looks like this:

SQL
GRANT USAGE  ON DATABASE sales_db                 TO ROLE analyst_r;
GRANT USAGE  ON SCHEMA   sales_db.public          TO ROLE analyst_r;
GRANT SELECT ON TABLE    sales_db.public.orders   TO ROLE analyst_r;

Two shortcuts worth knowing. GRANT <priv> ON ALL ... IN SCHEMA covers objects that already exist. GRANT <priv> ON FUTURE ... IN SCHEMA covers objects created later. The future-grant pattern is what production teams use to onboard new tables automatically without manually re-granting on every new object.

Micro-Concept 5: Account Roles vs Database Roles

Until a few years ago, every role in Snowflake was account-wide. Snowflake now distinguishes two role types, and C03 tests the difference directly.

 Account roleDatabase role
ScopeWhole account. Privileges on any object.One database. Privileges only on objects inside it.
Created withCREATE ROLE my_role;CREATE DATABASE ROLE my_db.my_role;
Can be activated in a session?Yes. USE ROLE my_role;No. Must be granted to an account role and used through it.
Granted toUsers or other rolesAccount roles (or other database roles in the same database)
LifecycleIndependent of any databaseTied to the database. Dropped when the database is dropped.

Database roles exist so a database owner can manage access for their database without touching the account-wide role hierarchy. A common pattern: define a database role db_reader with USAGE on the database, USAGE on every schema, and SELECT on every table. Then grant that one database role to whichever account roles need to query the database, for example an analyst_r for one team and a reporting_r for another. The database owner publishes a single, well-scoped read role. The account hierarchy stays clean.

The trap that lands on the exam: an option claiming you can activate a database role directly with USE ROLE my_db.my_db_role. That is false. Database roles are only usable through an account role they have been granted to. In SnowPro training sessions I have run, this is consistently the question where the room splits 50/50 on the first attempt.

Micro-Concept 6: PUBLIC, the Default Floor

PUBLIC is a special pseudo-role with four properties:

  • Every user is automatically granted PUBLIC. No explicit grant is needed.
  • Every role automatically inherits PUBLIC.
  • PUBLIC cannot be dropped.
  • Anything granted to PUBLIC is effectively granted to every user in the account.

Used deliberately, PUBLIC is the right place for grants that genuinely apply to everyone. One example: USAGE on the SNOWFLAKE database so all users can read ACCOUNT_USAGE views. Used carelessly, it is a security hole. Granting SELECT on a sensitive table to PUBLIC opens that table to the entire account. Grants to PUBLIC are easy to add and hard to walk back once business teams start relying on what they can see.

Micro-Concept 7: UBAC, the Third Model

Current Snowflake docs name a third model alongside DAC and RBAC: UBAC, User-Based Access Control. UBAC lets you grant a privilege directly to a user, bypassing roles entirely. It only takes effect when the user runs with USE SECONDARY ROLES = ALL. That is why it is narrow by design, aimed at private development, personal Streamlit apps, and similar single-user scenarios.

For C03 as it currently stands, the standard answer to “which access-control models does Snowflake use?” is DAC + RBAC. That is the exact pair in the official practice bank (Q18). Recognize UBAC if it shows up as a distractor or an “all that apply” option. Pick the DAC + RBAC pair when the question asks for Snowflake’s primary access-control framework.

Cheat Sheet

ConceptWhat to remember
RBAC one-linerPrivileges → Roles → Users. Two-step grant flow.
DAC one-linerEach object has an owner role with OWNERSHIP; the owner grants access on it.
Snowflake uses…Both DAC and RBAC together. UBAC exists for narrow cases.
Sample Q4 answerOwner-grants-access framework = Discretionary Access Control (DAC).
Who owns objects?Roles, not users. The role active during CREATE becomes the owner.
OWNERSHIP transferGRANT OWNERSHIP ON <obj> TO ROLE <other_role>;
Read-a-table privilegesDatabase USAGE + Schema USAGE + Table SELECT. Outermost → innermost.
Future grantsGRANT ... ON FUTURE TABLES IN SCHEMA ... applies to objects created later.
Role inheritanceGrant role to role; parent inherits child’s privileges. Broadest role sits on top.
Account rolesAccount-wide; USE ROLE-activatable; CREATE ROLE.
Database rolesScoped to one database; NOT USE ROLE-activatable; granted to account roles; CREATE DATABASE ROLE.
PUBLIC roleEvery user has it; every role inherits it; cannot be dropped. Grants to PUBLIC = everyone.
UBACPrivileges granted directly to a user. Active only with USE SECONDARY ROLES = ALL.
🎯 Exam Tip

Four near-miss traps to recognize on the exam:

(1) “In RBAC, privileges are granted directly to users.” False. RBAC’s defining property is the two-step Privilege → Role → User flow. Direct-to-user grants are UBAC, which only activate with USE SECONDARY ROLES = ALL. (2) “Users own the objects they create.” False. Roles own objects. The role active during CREATE is the owner. This trap catches engineers coming from Postgres or Oracle, where the owner is a user. (3) “Database roles can be activated in a user session with USE ROLE.” False. Database roles only work through an account role they have been granted to. USE ROLE my_db.my_db_role errors out. (4) “Snowflake only uses RBAC.” False. DAC and RBAC coexist, and UBAC is a narrower third option. When Sample Q4 phrasing appears (owner grants access on objects they own), answer DAC. When the stem says “privileges are organized via roles,” answer RBAC. When it asks about Snowflake’s access control framework without further qualification, the safe answer is DAC and RBAC together.

🛠️

Hands-On Lab

Type: LAB (guided)  |  Time: ~15 minutes  |  Credits: <0.05  |  Prerequisite: ACCOUNTADMIN role available (default in trial).
1

Create two functional roles and plumb them into the SYSADMIN hierarchy.

SQL
USE ROLE ACCOUNTADMIN;
USE WAREHOUSE lab_xs;

-- Create two custom account roles
CREATE ROLE IF NOT EXISTS analyst_r;
CREATE ROLE IF NOT EXISTS engineer_r;

-- Grant them into SYSADMIN so they sit under SYSADMIN's hierarchy.
-- Snowflake best practice: every custom role rolls up to SYSADMIN.
GRANT ROLE analyst_r  TO ROLE SYSADMIN;
GRANT ROLE engineer_r TO ROLE SYSADMIN;

-- Grant warehouse usage so the roles can actually query
GRANT USAGE ON WAREHOUSE lab_xs TO ROLE analyst_r;
GRANT USAGE ON WAREHOUSE lab_xs TO ROLE engineer_r;

-- Grant the roles to yourself so you can switch between them
GRANT ROLE analyst_r  TO USER IDENTIFIER(CURRENT_USER());
GRANT ROLE engineer_r TO USER IDENTIFIER(CURRENT_USER());
👀 Observe: Two custom account roles, plumbed into SYSADMIN, with warehouse access, granted to you. Rolling custom roles up to SYSADMIN is the standard Snowflake recommendation. If you skip it, an admin operating as SYSADMIN will not be able to see or manage the objects those roles create.
2

Demonstrate DAC ownership. Create a database under one role and check who Snowflake records as the owner.

SQL
USE ROLE engineer_r;
CREATE DATABASE rbac_lab;

-- Confirm engineer_r owns the database (NOT the user)
SHOW DATABASES LIKE 'rbac_lab';
-- Look at the "owner" column — it shows ENGINEER_R, the role.

USE ROLE analyst_r;
-- analyst_r doesn't own rbac_lab and has no privileges on it
SHOW DATABASES LIKE 'rbac_lab';
-- Returns 0 rows — analyst_r can't even see it.
👀 Observe: The owner column on SHOW DATABASES shows a role name, not a user name. That is DAC’s defining property in one screen. The fact that analyst_r sees zero rows is default-deny in action: no privilege, no visibility.
3

The owner role grants access to a second role without involving ACCOUNTADMIN.

SQL
-- engineer_r is the owner — it can grant without needing ACCOUNTADMIN
USE ROLE engineer_r;
GRANT USAGE ON DATABASE rbac_lab          TO ROLE analyst_r;
CREATE SCHEMA rbac_lab.test_schema;
GRANT USAGE ON SCHEMA rbac_lab.test_schema TO ROLE analyst_r;

CREATE TABLE rbac_lab.test_schema.sample_data (id INT, label STRING);
INSERT INTO rbac_lab.test_schema.sample_data VALUES (1, 'alpha'), (2, 'beta');
GRANT SELECT ON TABLE rbac_lab.test_schema.sample_data TO ROLE analyst_r;

-- Now analyst_r has the full three-level read chain
USE ROLE analyst_r;
SELECT * FROM rbac_lab.test_schema.sample_data;
-- Returns the two rows. The three privileges (DB USAGE, SCHEMA USAGE, TABLE SELECT) combine.
👀 Observe: engineer_r, not ACCOUNTADMIN, granted access on objects it owns. That is DAC’s “discretionary” in action: the owner role decides on its own. The three-tier privilege chain from Micro-Concept 4 is what makes the SELECT work. Remove any one of the three grants and the query fails.
4

Prove default-deny: analyst_r can read, but cannot create.

SQL
USE ROLE analyst_r;
-- Should fail: analyst_r has USAGE on the database but no CREATE SCHEMA privilege
CREATE SCHEMA rbac_lab.analyst_attempt;
-- ERROR: Insufficient privileges to operate on database 'rbac_lab'
👀 Observe: USAGE is read-only access to the container. It does not include CREATE. For analyst_r to create schemas, engineer_r would need to GRANT CREATE SCHEMA ON DATABASE rbac_lab TO ROLE analyst_r. Nothing is implicit. Every privilege is named and granted, or it does not exist.
5

Create a database role and watch the activation restriction in action.

SQL
USE ROLE engineer_r;
-- A database role lives INSIDE rbac_lab
CREATE DATABASE ROLE rbac_lab.read_role;
GRANT USAGE  ON SCHEMA rbac_lab.test_schema                 TO DATABASE ROLE rbac_lab.read_role;
GRANT SELECT ON TABLE  rbac_lab.test_schema.sample_data     TO DATABASE ROLE rbac_lab.read_role;

-- Try to activate it directly — this should FAIL
USE ROLE rbac_lab.read_role;
-- ERROR: Database roles cannot be activated as a primary role.

-- Correct pattern: grant the database role to an ACCOUNT role
GRANT DATABASE ROLE rbac_lab.read_role TO ROLE analyst_r;
-- Now anyone using analyst_r inherits read_role's privileges
USE ROLE analyst_r;
SELECT COUNT(*) FROM rbac_lab.test_schema.sample_data;
-- Works — via the inherited database role.
👀 Observe: The intentional USE ROLE rbac_lab.read_role failure is the whole point. A multiple-choice option that lets you activate a database role directly is wrong every time. Run this step yourself. The error message is the one you want to recognize on exam day.
6

Transfer OWNERSHIP and watch the role in the metadata change.

SQL
USE ROLE engineer_r;
-- Transfer ownership of the sample_data table to analyst_r
GRANT OWNERSHIP ON TABLE rbac_lab.test_schema.sample_data TO ROLE analyst_r;

-- engineer_r no longer owns the table — it can't even modify it now
SHOW TABLES LIKE 'sample_data' IN rbac_lab.test_schema;
-- Check the "owner" column — now ANALYST_R.
👀 Observe: Ownership moved with one GRANT statement. Exactly one role owns the object at any time. The moment the new grant lands, the previous owner loses OWNERSHIP. Production practice pairs this with COPY CURRENT GRANTS so dependent grants do not vanish, but for the lab a plain transfer is enough to make the point.
7

Cleanup. Database roles drop with their parent database; account roles need explicit DROP.

SQL
USE ROLE ACCOUNTADMIN;
DROP DATABASE IF EXISTS rbac_lab;        -- drops the database role too
DROP ROLE IF EXISTS analyst_r;
DROP ROLE IF EXISTS engineer_r;
-- lab_xs warehouse stays; day10_orders stays.
💡 Pro tip: Dropping a role automatically revokes every grant it held. That is convenient in a dev account and dangerous in production. Before dropping a role in a live environment, transfer its OWNERSHIP grants to a successor role and re-grant any privileges the team still needs. Otherwise users dependent on that role lose access the moment the DROP commits.
❄️

Snowflake Documentation

🔗

External References

Practice Questions

Options:

A. Discretionary Access Control (DAC)
B. Identity Access Management (IAM)
C. Mandatory Access Control (MAC)
D. Role-Based Access Control (RBAC)
E. Security Assertion Markup Language (SAML)

✅ Answer: A, D

Why A and D: Snowflake’s access control framework is built on two models named in the official docs. Discretionary Access Control (DAC) gives each object an owner role that can grant access on it. Role-Based Access Control (RBAC) grants privileges to roles, and roles to users. UBAC is now also recognized as a third model. The official C03 practice answer key still uses the DAC + RBAC pair, so that is the safe two-option pick.

Why not B: IAM is AWS terminology for cloud identity management. It is not a generic access-control model and is not Snowflake’s term.

Why not C: MAC (Mandatory Access Control) uses centrally enforced labels and policies, typical in government and defence systems. Snowflake does not implement MAC.

Why not E: SAML is a federated authentication protocol: how a user proves who they are. Access control is what happens after that. Different layer of the security stack entirely.

Options:

A. Role-Based Access Control (RBAC)
B. Discretionary Access Control (DAC)
C. Mandatory Access Control (MAC)
D. Attribute-Based Access Control (ABAC)

✅ Answer: B

Why B: DAC is defined by exactly this property: each object has an owner that can discretionarily grant access on it. This is the official C03 sample exam Q4 answer almost verbatim. Anchor on the cue word “owner” in the stem.

Why not A: RBAC is also part of Snowflake’s access control, but RBAC describes how privileges flow through roles to users. It does not specifically describe owners granting access on objects they own. Mixing the two is the most common exam trap on this question type.

Why not C: MAC uses centrally enforced labels and policies. There is no owner-discretion concept in MAC, so it cannot fit the stem.

Why not D: ABAC bases decisions on attributes such as department, time of day, or IP address. Snowflake does not implement ABAC as a primary model.

Options:

A. A database role can be activated in a user session with USE ROLE, just like an account role.
B. A database role is scoped to a single database; it cannot be activated directly and must be granted to an account role to be used.
C. Database roles are stored in the SNOWFLAKE database and apply to every account in the organization.
D. Database roles replace account roles entirely; account roles are deprecated.

✅ Answer: B

Why B: Database roles are scoped to one database and their privileges only apply inside that database. They cannot be activated as a primary role. The only way to use one is to grant it to an account role and activate the account role. Re-read Micro-Concept 5 if missed.

Why not A: The most-tested trap on database roles. USE ROLE my_db.my_db_role errors out by design. If an option offers this, it is wrong every time.

Why not C: Database roles live inside their owning database, not in the SNOWFLAKE shared database. They do not span accounts.

Why not D: Account roles are not deprecated. Both role types coexist, and account roles remain the foundation of the system role hierarchy.

Options:

A. Whoever ran the CREATE statement (Alice or Bob) is the personal owner of the table.
B. The role analyst_r owns the table; both Alice and Bob can manage it because both have analyst_r.
C. ACCOUNTADMIN automatically owns all newly created tables.
D. PUBLIC owns the table by default.

✅ Answer: B

Why B: Snowflake’s DAC implementation puts ownership on the role, not on the user. The role active during CREATE becomes the owner, and anyone holding that role can exercise the OWNERSHIP privilege: granting, modifying, dropping. Re-read Micro-Concept 3 if missed.

Why not A: The “user owns what they create” assumption carries over from Postgres and Oracle. In Snowflake it is wrong. If Alice’s user account is dropped, the table does not become orphaned. analyst_r still owns it.

Why not C: ACCOUNTADMIN has broad authority but does not automatically own newly created objects. The role active in the creating session is the owner, full stop.

Why not D: PUBLIC is the default-floor pseudo-role for grants that should apply to everyone. It never auto-owns user-created objects.

Options:

A. 1) Schema USAGE   2) Database USAGE   3) Table USAGE
B. 1) Schema USAGE   2) Database USAGE   3) Table SELECT
C. 1) Database USAGE   2) Schema USAGE   3) Table SELECT
D. 1) Database USAGE   2) Schema USAGE   3) Table INSERT

✅ Answer: C

Why C: Privileges flow through the container hierarchy from the outside in. To read a table you need USAGE on the database, then USAGE on the schema, then SELECT on the table. Missing any one of the three blocks the query. Re-read Micro-Concept 4 if missed.

Why not A: USAGE is not a valid privilege on tables. It applies to schemas, databases, warehouses, integrations, and functions. The option is constructed to catch readers who memorize “USAGE” without remembering which object types accept it.

Why not B: The privilege names are right but the order is wrong. Schema USAGE before Database USAGE is impossible. Without database USAGE, the schema is not even reachable. Order is part of the answer on this question type.

Why not D: INSERT is a real table privilege but it grants the right to write new rows, not read existing ones. The privilege required to query is SELECT. Classic distractor swap.

📝 Recap

Today you learned: Snowflake runs two access-control models side by side. RBAC grants privileges to roles and roles to users in a strict two-step flow. Privileges are never granted directly to users. DAC gives every object an owner role with the OWNERSHIP privilege, and that owner role can grant access on the object at its own discretion. The owner is always a role, not a user. The role active during CREATE becomes the owner, and OWNERSHIP can be transferred to another role with a single grant. To query a table, a role needs USAGE on the database, USAGE on the schema, and SELECT on the table, outermost to innermost. Account roles are account-wide and activatable with USE ROLE. Database roles are scoped to one database and cannot be activated directly: they must be granted to an account role. PUBLIC is the built-in pseudo-role every user inherits. UBAC is a newer third model that grants privileges directly to a user, and it only takes effect with USE SECONDARY ROLES = ALL. For C03, the standard pair remains DAC + RBAC.

Key takeaway: Owner grants access → DAC. Privileges go through roles → RBAC. Which models does Snowflake use → both. Every Domain 2 access-control question on the exam decomposes into one of these three cues plus one privilege-hierarchy check (Database USAGE → Schema USAGE → Table SELECT).

Tomorrow (Day 19): System Roles + Custom Roles + Secondary Roles. We zoom into the role layer: the six system roles (ORGADMIN, ACCOUNTADMIN, SECURITYADMIN, USERADMIN, SYSADMIN, PUBLIC) and what each can actually do. The traps include ACCOUNTADMIN is not a superuser, SECURITYADMIN (not SYSADMIN) creates network policies, and USERADMIN is the standard role for creating users and roles. Plus the secondary-role mechanism (USE SECONDARY ROLES ALL | NONE | role1, role2) that lets one user combine privileges from multiple roles in a single session.

Abhay Krishnan

Abhay Krishnan

Senior Data & AI Consultant
Connect on LinkedIn

With over five years of data engineering experience at EY and Infosys, Abhay Krishnan specializes in building scalable data pipelines and cloud warehousing solutions. He is a certified SnowPro Core professional, alongside credentials in AWS and Azure. Abhay created this 50-day track to solve a problem he faced firsthand: the lack of a structured, free resource for Snowflake certification prep. Follow him on LinkedIn for more data engineering insights.