Snowflake roles decide what a role can read. They can’t tell which analyst is behind the BI service account that holds the role, and Snowflake’s own audit trail names that shared user. Formal adds Snowflake access control at the proxy, on top of native Snowflake security: every session, query and result is checked against the person or agent who sent it.
How does Formal extend Snowflake access control beyond RBAC?
In Snowflake RBAC, privileges are granted to roles and roles are granted to users. That model gets thin when many people share one Snowflake user (a BI tool, an internal app, an agent), and when the same rules must also hold on Postgres or MySQL.
Formal’s Connector is a reverse proxy in your VPC that works as a Snowflake proxy for your clients. People and agents log in with their Formal identity, and the Connector authenticates to Snowflake with a Native User credential it holds, so nobody handles Snowflake secrets. Snowflake still enforces the grants on that Native User: your roles stay the outer boundary.
On top, PAM policies written in Rego run at session (login), request (before the query runs) and response (on the rows coming back). They can use the Formal user, their groups, the BI end user and the Snowflake role used to connect (input.snowflake.role). One policy can cover several databases with included_technologies := ["postgres", "mysql", "snowflake"].
How does Snowflake data masking work through Formal?
Snowflake’s native Dynamic Data Masking uses masking policies: schema-level objects attached to columns and evaluated at query time by role and execution context (Enterprise Edition or higher). The MASKING_POLICIES and POLICY_REFERENCES views show which masking policy is set where.
Formal masks at the Connector, after Snowflake returns the rows. The decision uses the Formal identity and groups, including the end user behind a BI tool (input.end_user.groups), so two analysts on the same Metabase connection can see different values. Mask types include nullify, salted or deterministic hashing (deterministic keeps joins working), fake values and redaction.
The two layers stack: Snowflake applies its masking policies first, then Formal masks what comes back. Drivers download large result sets as chunks from cloud storage; the Connector proxies those downloads through its s3. subdomain and masks the chunks too. For masking across databases, see data masking.
Who ran the query behind a shared BI service account?
Most BI tools connect to Snowflake as one service account, and Snowflake’s ACCESS_HISTORY view records the user that ran each statement: that shared user. Formal resolves the person instead, through end-user identity propagation:
- Omni sends a
QUERY_TAGwith the user’s Omni ID at login. Every query in that session belongs to that user. - Metabase, Sigma, Hex and custom apps append a SQL comment with the user’s external ID to each query.
- Looker passes the user’s email in the JDBC connection options (
formal_enduser).
The Connector maps each ID to a Formal user, then policies evaluate input.end_user and logs show the end user next to the machine user. Setup per tool is in the BI integrations guide. For why this matters when the BI tool itself is the target, read the Metabase zero day.
How do you keep Snowflake audit logs across databases?
Every Snowflake query through the Connector is logged with the Formal user, the end user and their groups, the query as received and as sent after any rewrite, and the policies it triggered. It lands in the same log as your other resources, and log integrations export it to S3, Google Cloud Storage, Azure Blob Storage, Datadog, Splunk or Panther.
Keep Snowflake’s access history for column lineage inside the warehouse; use Formal’s log to answer who, across systems. ManoMano uses Formal across Metabase, Snowflake and Postgres. Its case study reports full visibility into data access patterns across the three, policies enforced on Okta groups, and former employees found with active Metabase jobs.
How do approvals and time-bound Snowflake access work?
Instead of granting a role for good, create a policy suspension: an exception to one policy for one user or group, limited to matching requests and a time window. Approving an access request creates one, and Workflows can create one as an action.