Architecting Secure Access in Snowflake

Snowflake makes analytics easy—but securing access is where the real challenge lies. How do you give teams the freedom to explore data without exposing your entire warehouse? In this chapter, we tackle best practices for account design and Role-Based Access Control (RBAC) in Snowflake, while also exploring how to manage access grants at scale.
Foundations of Access Control
Snowflake’s access model is built on three foundational layers:
DAC (Discretionary Access Control): Object owners can grant privileges on what they own.
RBAC (Role-Based Access Control): The recommended model—privileges are assigned to roles, which are granted to users.
UBAC (User-Based Access Control): You can grant privileges directly to users, but this quickly becomes hard to scale and audit.
Objects in Snowflake live in a hierarchy: database → schema → object
. Privileges can be granted at any level, and RBAC is the cleanest way to control access through role inheritance.
The Principle of Least Privilege
Always start with only the access a user or tool absolutely needs—and nothing more. This reduces risk, simplifies audits, and makes your security model easier to manage.
A Clean Role Hierarchy
A layered role hierarchy decouples users from direct privileges, making your permission model scalable and auditable. Here’s how to think about it:
System Roles
Pre-defined roles likeACCOUNTADMIN
andSYSADMIN
. Use these sparingly, and only for infrastructure-level administration.Functional Roles
Roles likeanalyst_role
,data_engineer_role
, etc. These represent job functions and are granted to users.Access Roles
Granular, purpose-specific roles likehr_read
,finance_write
, orproduct_view
. These are assigned to Functional Roles.Objects
Tables, views, and schemas—accessed through the chain of roles.
The inheritance flows upward:USER → FUNCTIONAL ROLE → ACCESS ROLE → OBJECTS
Role Hierarchy: A Practical Example
Let’s say we need to give a data analyst named Richard read-only access to all tables in the hr.public
schema. Here’s how we’d build that access structure from the bottom up:
Step 1: Create the Access Role
This role holds the exact privilege: read access to the HR tables.
-- Access Role with SELECT on HR tables
create role hr_read;
grant select on all tables in schema hr.public to role hr_read;
Step 2: Create the Functional Role and Assign the Access Role
This role represents the analyst’s job function. It inherits the hr_read
permission.
-- Functional Role for analysts
create role analyst_role;
grant role hr_read to role analyst_role;
Step 3: Assign the Functional Role to the User
-- Assign to user Richard
grant role analyst_role to user richard;
Richard now indirectly has SELECT access on the HR tables. If he also needs access to sales
, simply create a sales_read
role and grant it to analyst_role
. No need to touch the user again.
This modular design makes permission changes clean and scalable.
Managed Access Schemas
By default, anyone with sufficient privileges on a schema can grant access. This can quickly spiral into a web of untraceable permissions.
A managed access schema centralizes control. Only the schema owner or roles with MANAGE GRANTS
can assign privileges inside it.
-- Enable central access control
create schema finance.managed_schema with managed access;
Managed access ensures only authorized roles can control object access, making audits and reviews much easier.
Future Grants: Scale Without Manual Overhead
When new tables or views are created, they don’t inherit privileges by default. Future Grants solve this:
grant select on future tables in schema analytics.reporting to role report_read;
Now, whenever a new table is added to analytics.reporting
, the report_read
role automatically gets access.
UBAC: When Direct Grants Make Sense
While RBAC is preferred, User-Based Access Control (UBAC) can still be useful in limited situations:
Temporary sandboxing
One-off experiments
Emergency overrides
Example:
-- Grant ad-hoc access directly to a user
grant select on table dev.experiment to user alice;
Use sparingly—and always track these direct grants to avoid gaps in your security model.
Tracking and Managing Access
Snowflake provides system views to help you review access patterns:
1. What roles can access what?
select *
from snowflake.account_usage.grants_to_roles
where role_name = 'ANALYST_ROLE';
2. Who accessed what and when?
select *
from snowflake.account_usage.access_history
where object_name = 'FCT_ORDERS'
order by query_start_time desc;
If a role has broad privileges but isn’t using them, it's a signal to tighten access.
Example: Service Role for BI Tool
-- Create a role for a BI tool like Tableau
create role tableau_read;
-- Grant access only to the reporting schema
grant select on all tables in schema reporting.prod to role tableau_read;
-- Assign role to service user
grant role tableau_read to user tableau_service_user;
This follows the same modular RBAC model. The BI tool sees only what it needs.
Use System Roles Cautiously
Avoid assigning ACCOUNTADMIN
, SYSADMIN
, USERADMIN
, or SECURITYADMIN
to everyday users. Use these only for their intended scope:
ACCOUNTADMIN: Overall account management
SYSADMIN: Object creation and configuration
SECURITYADMIN: User and role control
USERADMIN: Managing users and roles (avoid assigning to the same person as SECURITYADMIN)
Instead, use custom roles for actual work and reserve system roles for periodic administrative use.
Final Thoughts
A secure Snowflake account starts with clean role architecture and consistent practices. Follow these pillars:
Use RBAC with modular roles
Define managed schemas to control access centrally
Enable future grants for automation
Track and audit usage with ACCOUNT_USAGE views
Limit UBAC to rare, auditable cases
By taking a layered and auditable approach to access, you create a warehouse where teams move fast—without compromising control.
Subscribe to my newsletter
Read articles from Sriram Krishnan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
