[New Feature] Snowflake's Restricted Session Scope is Now Generally Available - Narrowing Effective Permissions Only During CoCo Usage

[New Feature] Snowflake's Restricted Session Scope is Now Generally Available - Narrowing Effective Permissions Only During CoCo Usage

Snowflake's Restricted Session Scope (RSS) has become generally available. We will verify it from CoCo in Snowsight across four use cases anticipated in real-world practice.
2026.09.04

This page has been translated by machine translation. View original

This is Kawabata.

On September 3, 2026, Restricted Session Scope (RSS) became generally available.
I will verify it with 4 use cases expected in practice using CoCo in Snowsight.

https://docs.snowflake.com/en/user-guide/restricted-session-scope

【Update】
I was selected as a finalist in the "RISING COMMUNITY LEADER OF THE YEAR" category (APJ slot) of the Snowflake Community Awards.
Please check the details at the link below.

https://dev.classmethod.jp/articles/snowflake-community-awards-finalist-activities-review/

Conclusion First

  • RSS is a mechanism that narrows effective privileges only for "sessions where an agent is active" without changing any existing RBAC. Agents can only execute operations that are "permitted by both RBAC and RSS" — privileges never increase
  • By excluding writes to the production database from the RSS allowed scope and prohibiting switching to privileged roles, agent sessions can be restricted to near read-only on a per-user basis
  • data write only targets data writes to tables and does not include DDL such as CREATE TABLE. If you want to allow table creation as well, add object management
  • RSS, which narrows "what can be touched," and agent-aware masking, which narrows "what values are visible after being touched," are complementary and can be layered in the same session
  • In this verification, we confirmed that even with the same user and same role, INSERT to production tables, DDL, and role switching are rejected in CoCo's agent sessions, while they execute according to RBAC privileges in worksheets

Overall Picture

RSS covers "the scope of what can be touched while an agent is running" within Snowflake's governance features.

Mechanism Control Axis Unit
RBAC Who can do what Role
Feature Policy Which objects can be created Containers such as DB
Masking / Projection / Aggregation Policies Which values in touched data are visible Column / Table
RSS Which operations / ranges can be touched when using an agent Session (when agent is active)

Based on this positioning, this article proceeds in the following order.

  1. Previous challenges
  2. What is RSS
  3. Verification
  4. Closing

Previous Challenges

Snowflake's access control is based on RBAC. Assign privileges to roles, assign roles to users. Since this mechanism determines privileges at the "who" level, the available privileges are the same whether the same user operates from a worksheet as a person or operates via an agent.

If an analytics role designed for human use has INSERT or CREATE TABLE attached, CoCo can perform the same operations. Furthermore, CoCo starts sessions with the user's default role, but if instructed to "switch roles" during a conversation, it can execute USE ROLE. Although a confirmation dialog appears, there was no way to prohibit this in advance.

There is also a method to narrow "visible values" using a masking policy conditioned on IS_AGENT_ACTIVATED, but this is about "after touching."

I have verified this below, so please check it if you are curious about the details.
https://dev.classmethod.jp/articles/snowflake-is-agent-activated-coco-pii-masking/

RSS is what can narrow "the scope of what can be touched" itself, only when an agent is active.

What is RSS

RSS is a mechanism that defines the privilege ceiling for agents. Custom RSS can be defined in YAML, and Snowflake predefined scopes can also be referenced by name. It does not change the user's RBAC at all. In sessions where an agent is active, only operations permitted by both RBAC and RSS can be executed. If either one does not permit it, that operation cannot be performed.

Definition Structure

The RSS definition consists of 2 sections: privilege_scopes, which defines operations and target scope, and role_scopes, which defines available roles.

privilege_scopes:
  allowed_privileges:
    - privileges: [<group privilege>, ...]
      account: [all]                 # or databases: [...] / schemas: [...]
    - privileges: [data write]
      databases: [SANDBOX_DB, USER$] # USER$ resolves to the session user's personal DB
role_scopes:
  blocked_roles: [ACCOUNTADMIN, SYSADMIN]   # or allowed_roles
  blocked_secondary_roles: [ACCOUNTADMIN]    # or allowed_secondary_roles
  allow_role_switching: false                # default is true

For privileges, you write the following group privileges rather than individual privilege names.

Group Privilege Target Official Documentation Definition
data read All containers SELECT from tables, views, streams, stages, and workspaces
data write All containers Data writes to tables (does not include DDL)
object discovery All containers Object discovery via SHOW, etc. (does not read data)
compute usage Account Use of warehouses and compute pools
program usage All containers Execution of UDFs, stored procedures, and Streamlit
grant management All containers GRANT / REVOKE
object management All containers Full control of non-sensitive objects
full management Account All operations within the account

What you want to keep in mind here is that data write is "data writes to tables" and does not include DDL such as CREATE TABLE.

How to Specify

To use RSS, specify RSS in the AGENT_RESTRICTED_SESSION_SCOPE of a session policy and attach that policy to an account or user. There are 3 ways to specify it.

-- (a) Reference a Snowflake predefined scope by name
CREATE SESSION POLICY sp_a
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ';

-- (b) Reference a custom RSS object by fully qualified name
CREATE RESTRICTED SESSION SCOPE gov.policies.my_scope AS $$ ... $$;
CREATE SESSION POLICY sp_b
  AGENT_RESTRICTED_SESSION_SCOPE = 'GOV.POLICIES.MY_SCOPE';

-- (c) Embed YAML inline
CREATE SESSION POLICY sp_c
  AGENT_RESTRICTED_SESSION_SCOPE = $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read]
      account: [all]
$$;

-- Apply to the entire account or per user
ALTER ACCOUNT SET SESSION POLICY sp_a;
ALTER USER jsmith SET SESSION POLICY sp_b;

There are 3 types of Snowflake predefined scopes.

Scope Name Permitted Operations Prohibited Operations
SNOWFLAKE$DATA_READ data read across the entire account Writes, DDL, UDFs, stored procedures
SNOWFLAKE$DATA_READ_WITH_AI data read + object discovery + USAGE for AI-related objects (Agent, MCP Server, Cortex Search service) Writes, DDL, stored procedures
SNOWFLAKE$DATA_READ_PROGRAM_USAGE data read + program usage (execution of UDFs, stored procedures, Streamlit apps) Writes, DDL

For SNOWFLAKE$DATA_READ_WITH_AI, there is a discrepancy in UDF handling between the official documentation body text (which reads as including UDF USAGE) and the appendix YAML definition (only Agent, MCP Server, Cortex Search service). In this verification, UDF calls were rejected (described later).

When RSS is Applied

RSS is applied only when an agent is active (agent-activated) in the session. As situations where SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') returns TRUE, the official documentation lists the following.

  • Snowflake native agents: Cortex Agents, lite agents via each CoCo client (CLI / Desktop / Snowsight), CoWork, calls via REST API
  • External agents: Connections via Snowflake-managed MCP Server, users of type SERVICE_AGENT, OAuth security integrations with IS_AGENTIC = TRUE

In sessions that do not go through an agent, such as worksheets or dbt, RSS is ignored.

Which RSS is applied can be confirmed within the session using the following function.

SELECT SYS_CONTEXT('SNOWFLAKE$CURRENT', 'ACTIVE_RESTRICTED_SESSION_SCOPES');

Limitations

These are specifications described in the Considerations section of the official documentation.

Limitations
  • RSS is an upper limit on existing RBAC and does not add privileges
  • Once RSS is activated for agent activity, the RSS cannot be changed, removed, or elevated during that session. To apply a different scope, start a new session (for CoCo, a new conversation)
  • If there is a user-level session policy, only its AGENT_RESTRICTED_SESSION_SCOPE is used. It is not merged with account-level policies
  • If the session policy does not have AGENT_RESTRICTED_SESSION_SCOPE, no scope is applied. For non-agent operations, this setting is ignored
  • Warehouse usage is implicitly permitted regardless of privilege scope
  • blocked_roles is an exact match on role name. It does not traverse the role hierarchy to block inherited sources, nor does it strip privileges already inherited by the current primary role
  • An RSS referenced by fully qualified name from a session policy cannot be dropped until the reference is removed
  • The feature for users to manage their own RSS (user-managed RSS) in CoCo CLI / CoCo Desktop is in Private Preview as of September 4, 2026. The RSS via session policy covered in this article is GA

Prerequisites

  • Snowflake: Enterprise edition, AWS Tokyo region
  • Required privileges
    • Creating RSS: CREATE RESTRICTED SESSION SCOPE privilege on the target schema. Modification requires MODIFY or OWNERSHIP on the RSS, deletion requires OWNERSHIP
    • Creating session policy: CREATE SESSION POLICY privilege on the target schema
    • Applying session policy: APPLY SESSION POLICY privilege on the account

Since the following SQL uses CREATE OR REPLACE, any databases, schemas, or tables with the same name will have their contents replaced. Please execute in a dedicated verification account or with dedicated object names.

All verifications are performed with ACCOUNTADMIN, and the session policy is applied only to my own user. Please note that executing ALTER ACCOUNT SET SESSION POLICY on a shared account will affect all users who use CoCo.

Verification Approach

For all use cases, the same SQL is executed in both CoCo (agent session) and worksheet (non-agent session), and we compare that RSS only acts on the agent. Results are shown in 4 columns — "RBAC," "RSS," "CoCo," and "Worksheet" — and we confirm that "only operations permitted by both RBAC and RSS pass through." CoCo and the worksheet use the same user, same default role, and same warehouse, with only the SQL content matched for comparison.

Verification Environment

We prepare the following 3 databases and a verification role that CoCo uses at startup.

  • KAWABATA_RSS_PROD_DB: Production-equivalent DB. Uses only the dedicated verification schema KAWABATA_RSS_SALES
  • KAWABATA_RSS_SANDBOX_DB: Sandbox DB. The agent's write destination
  • KAWABATA_RSS_GOV_DB: Governance DB. Storage location for RSS and policies
USE ROLE ACCOUNTADMIN;

CREATE OR REPLACE DATABASE KAWABATA_RSS_PROD_DB;     -- Production equivalent
CREATE OR REPLACE SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES;
CREATE OR REPLACE DATABASE KAWABATA_RSS_SANDBOX_DB;  -- Agent's write destination
CREATE OR REPLACE SCHEMA KAWABATA_RSS_SANDBOX_DB.WORK;
CREATE OR REPLACE DATABASE KAWABATA_RSS_GOV_DB;      -- Storage for RSS and policies
CREATE OR REPLACE SCHEMA KAWABATA_RSS_GOV_DB.POLICIES;

We insert 20 rows of fictitious data including PII columns (customer_name, email) into the production-equivalent orders table. The note column is a verification marker, and rows inserted by the agent are identified by 'RSS_TEST'.

INSERT statement for sample data (click to expand)
CREATE OR REPLACE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS (
  order_id      INT,
  customer_name STRING,
  email         STRING,
  order_date    DATE,
  product       STRING,
  amount        NUMBER(10,2),
  region        STRING,
  note          STRING
);

-- 20 rows of fictitious EC order data (all email addresses are example.com)
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS VALUES
  ( 1, 'Taro Sato',       'sato.taro@example.com',       '2026-08-01', 'Wireless Mouse',   2500.00, 'Kanto',    'SEED'),
  ( 2, 'Hanako Suzuki',   'suzuki.hanako@example.com',   '2026-08-01', 'USB-C Hub',        4800.00, 'Kansai',   'SEED'),
  ( 3, 'Ken Takahashi',   'takahashi.ken@example.com',   '2026-08-02', 'Monitor 27inch',  32000.00, 'Kanto',    'SEED'),
  ( 4, 'Misaki Tanaka',   'tanaka.misaki@example.com',   '2026-08-02', 'Keyboard',         9800.00, 'Chubu',    'SEED'),
  ( 5, 'Daisuke Ito',     'ito.daisuke@example.com',     '2026-08-03', 'Webcam',           6500.00, 'Kyushu',   'SEED'),
  ( 6, 'Sakura Watanabe', 'watanabe.sakura@example.com', '2026-08-03', 'Headset',          8900.00, 'Kanto',    'SEED'),
  ( 7, 'Sho Yamamoto',    'yamamoto.sho@example.com',    '2026-08-04', 'Laptop Stand',     3900.00, 'Kansai',   'SEED'),
  ( 8, 'Aya Nakamura',    'nakamura.aya@example.com',    '2026-08-04', 'Wireless Mouse',   2500.00, 'Tohoku',   'SEED'),
  ( 9, 'Takuya Kobayashi','kobayashi.takuya@example.com','2026-08-05', 'Docking Station', 18000.00, 'Kanto',    'SEED'),
  (10, 'Megumi Kato',     'kato.megumi@example.com',     '2026-08-05', 'USB-C Hub',        4800.00, 'Chubu',    'SEED'),
  (11, 'Naoki Yoshida',   'yoshida.naoki@example.com',   '2026-08-06', 'Monitor 27inch',  32000.00, 'Kansai',   'SEED'),
  (12, 'Yu Yamada',       'yamada.yu@example.com',       '2026-08-06', 'Keyboard',         9800.00, 'Hokkaido', 'SEED'),
  (13, 'Haruka Sasaki',   'sasaki.haruka@example.com',   '2026-08-07', 'Webcam',           6500.00, 'Kanto',    'SEED'),
  (14, 'Ryo Yamaguchi',   'yamaguchi.ryo@example.com',   '2026-08-07', 'Headset',          8900.00, 'Kyushu',   'SEED'),
  (15, 'Rina Matsumoto',  'matsumoto.rina@example.com',  '2026-08-08', 'Laptop Stand',     3900.00, 'Kansai',   'SEED'),
  (16, 'Makoto Inoue',    'inoue.makoto@example.com',    '2026-08-08', 'Wireless Mouse',   2500.00, 'Kanto',    'SEED'),
  (17, 'Ayaka Kimura',    'kimura.ayaka@example.com',    '2026-08-09', 'Docking Station', 18000.00, 'Chubu',    'SEED'),
  (18, 'Yuto Hayashi',    'hayashi.yuto@example.com',    '2026-08-09', 'USB-C Hub',        4800.00, 'Tohoku',   'SEED'),
  (19, 'Yui Saito',       'saito.yui@example.com',       '2026-08-10', 'Monitor 27inch',  32000.00, 'Kanto',    'SEED'),
  (20, 'Yosuke Shimizu',  'shimizu.yosuke@example.com',  '2026-08-10', 'Keyboard',         9800.00, 'Kansai',   'SEED');

We also create the write destination table on the sandbox side and the user-defined function (UDF) and stored procedure used for program usage determination.

CREATE OR REPLACE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (
  note_id    INT AUTOINCREMENT,
  note       STRING,
  created_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);

CREATE OR REPLACE FUNCTION KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO(v STRING)
  RETURNS STRING
  AS $$ 'echo: ' || v $$;

CREATE OR REPLACE PROCEDURE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING()
  RETURNS STRING
  LANGUAGE SQL
  EXECUTE AS CALLER
AS
$$
BEGIN
  RETURN 'pong from ' || CURRENT_ROLE();
END;
$$;

Verification Role and RBAC-side Privileges

We grant SELECT and INSERT on the target tables in production-equivalent and sandbox (also DELETE for production-equivalent), and CREATE TABLE on the target schemas to the verification role KAWABATA_RSS_ANALYST. We keep RBAC in a state where both can be written to, so we can see how much RSS can restrict.

CREATE OR REPLACE ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON WAREHOUSE WH_SI_JP TO ROLE KAWABATA_RSS_ANALYST;

-- Production equivalent: SELECT / INSERT / DELETE + CREATE TABLE
GRANT USAGE ON DATABASE KAWABATA_RSS_PROD_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE, CREATE TABLE ON SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES TO ROLE KAWABATA_RSS_ANALYST;
GRANT SELECT, INSERT, DELETE ON TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS TO ROLE KAWABATA_RSS_ANALYST;

-- Sandbox: SELECT / INSERT + CREATE TABLE
GRANT USAGE ON DATABASE KAWABATA_RSS_SANDBOX_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE, CREATE TABLE ON SCHEMA KAWABATA_RSS_SANDBOX_DB.WORK TO ROLE KAWABATA_RSS_ANALYST;
GRANT SELECT, INSERT ON TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES TO ROLE KAWABATA_RSS_ANALYST;

-- Governance: Execute UDF / stored procedure only
GRANT USAGE ON DATABASE KAWABATA_RSS_GOV_DB TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON SCHEMA KAWABATA_RSS_GOV_DB.POLICIES TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON FUNCTION KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO(STRING) TO ROLE KAWABATA_RSS_ANALYST;
GRANT USAGE ON PROCEDURE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING() TO ROLE KAWABATA_RSS_ANALYST;

GRANT ROLE KAWABATA_RSS_ANALYST TO USER KAWABATA_TOMOHIRO;

Since CoCo in Snowsight starts sessions with the user's default role, we temporarily replace the default role with the verification role only during verification. If the secondary role is ALL, the privileges of all roles the user has will be mixed into the RBAC side, so we leave it empty.

ALTER USER KAWABATA_TOMOHIRO SET DEFAULT_ROLE = KAWABATA_RSS_ANALYST;
ALTER USER KAWABATA_TOMOHIRO SET DEFAULT_SECONDARY_ROLES = ();

Let's Try It

Without RSS, the Agent Uses Full RBAC Privileges

First, we have CoCo execute each operation without a session policy attached.
We instructed CoCo as follows and had it execute the SQL one statement at a time without modifying it. The order_date of the test row contains CURRENT_DATE() at execution time, so the value changes depending on the execution date.

Please execute the following SQL statements one by one without modification,
and summarize the success/failure and results of each statement,
and if an error occurs, the full error message in a table.
If an error occurs, please continue to the next statement without stopping.
SELECT
  CURRENT_ROLE()            AS CURRENT_ROLE,
  CURRENT_SECONDARY_ROLES() AS CURRENT_SECONDARY_ROLES,
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS IS_AGENT_ACTIVATED,
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'AGENT_TYPE')         AS AGENT_TYPE;

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (901, 'Test Taro', 'test901@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');
CREATE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.AGENT_BASELINE (id INT);
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P0 baseline');
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P0');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();

2026-09-04_20h00_52

All 7 statements succeeded. CURRENT_ROLE is KAWABATA_RSS_ANALYST, IS_AGENT_ACTIVATED is TRUE, and even in the agent session, INSERT to production and CREATE TABLE pass as per RBAC. This is the state without RSS.

Production Read-Only, Writes Only to Sandbox

The first use case is "production read-only + sandbox writable," which is also in the official documentation examples. We allow data read and program usage for the entire account, and limit data write to the sandbox DB and personal DB.

USE ROLE ACCOUNTADMIN;

CREATE OR REPLACE RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY
  COMMENT = 'UC-A: Production read-only. Writes only to sandbox and personal DB'
  AS $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read, object discovery, program usage, compute usage]
      account: [all]
    - privileges: [data write]
      databases: [KAWABATA_RSS_SANDBOX_DB, USER$]
$$;

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY
  AGENT_RESTRICTED_SESSION_SCOPE = 'KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY';

-- Apply only to my own user
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

Note that warehouse usage is implicitly permitted regardless of privilege scope, so compute usage is only explicitly stated here for explanation purposes and works without it.

The created RSS can be confirmed with DESC to view the YAML directly. On the session policy side, AGENT_RESTRICTED_SESSION_SCOPE is displayed in addition to the conventional timeout and secondary role items.

DESC RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY;
DESC SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

2026-09-04_20h03_18

Open a new conversation in CoCo and have it execute the following SQL.

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (902, 'Test Jiro', 'test902@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');
CREATE TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.AGENT_T1 (id INT);
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P1 UC-A');
CREATE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_T1 (id INT);
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P1');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();

2026-09-04_20h06_33

Operation RBAC RSS CoCo Worksheet
SELECT production Permitted data read (account) Success Success
INSERT production Permitted No data write for production Rejected Success
CREATE TABLE production Permitted None Rejected Success
INSERT sandbox Permitted data write (sandbox) Success Success
CREATE TABLE sandbox Permitted data write does not include DDL Rejected Success
UDF / stored procedure Permitted program usage Success Success

The error upon rejection explicitly states which RSS was the cause.

SQL access control error: Insufficient privileges to operate on table 'ORDERS'.
Restricted session scope KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY does not include
INSERT access to TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS.

The verification role remains able to write to production at the RBAC level, and only writes via agent are stopped — exactly as intended.

Of course, executing it from a worksheet succeeds.
2026-09-04_20h07_55

data write and object management Are Different Things

data write is "data writes to tables" and does not include DDL such as CREATE TABLE. If you want to allow the agent to create tables in the sandbox, add object management. Existing RSS can be merged with YAML fragments using ADD. The official documentation syntax is ADD AS $$ ... $$, but this caused an unexpected 'AS' syntax error in the verification account, so we removed AS and executed it.

ALTER RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY ADD $$
privilege_scopes:
  allowed_privileges:
    - privileges: [object management]
      databases: [KAWABATA_RSS_SANDBOX_DB]
$$;

2026-09-04_20h14_40

Here is the result of executing CREATE TABLE in a new conversation.

2026-09-04_20h22_36

Only CREATE TABLE in the sandbox changed to success, and INSERT and CREATE TABLE to production remain rejected. Since RSS changes are not reflected in already open conversations, always verify in a new conversation.

Stopping Switching to Privileged Roles

The second use case addresses the "role switching" challenge mentioned at the beginning. We block ACCOUNTADMIN and others with role_scopes and prohibit role switching itself with allow_role_switching: false. We also stop enabling secondary roles with blocked_secondary_roles.

ALTER RESTRICTED SESSION SCOPE KAWABATA_RSS_GOV_DB.POLICIES.RSS_PROD_READONLY ADD $$
role_scopes:
  blocked_roles: [ACCOUNTADMIN, SYSADMIN, SECURITYADMIN]
  blocked_secondary_roles: [ACCOUNTADMIN, SYSADMIN, SECURITYADMIN]
  allow_role_switching: false
$$;

2026-09-04_20h29_38

In a new conversation, we have it try switching the primary role and enabling secondary roles in sequence. We click Allow on all Allow / Deny dialogs that CoCo presents, and observe whether the RSS side stops them.

USE ROLE ACCOUNTADMIN;
SELECT CURRENT_ROLE();
USE ROLE SYSADMIN;
SELECT CURRENT_ROLE();
USE ROLE KAWABATA_DBT_DEV_ROLE;   -- Not a blocked target, but role switching is prohibited
SELECT CURRENT_ROLE();
USE SECONDARY ROLES ALL;
SELECT CURRENT_SECONDARY_ROLES();
USE SECONDARY ROLES ACCOUNTADMIN;
SELECT CURRENT_SECONDARY_ROLES();

2026-09-04_20h31_36

2026-09-04_20h33_45

Even after clicking Allow in CoCo, all USE ROLE and USE SECONDARY ROLES commands were rejected with the following error. The primary role remained KAWABATA_RSS_ANALYST and the secondary roles remained empty and unchanged. Switching to KAWABATA_DBT_DEV_ROLE, which is not a blocked target, was also stopped by allow_role_switching: false.

Current session is restricted. USE ROLE not allowed.

blocked_roles is an exact match on role name and does not traverse the role hierarchy.
If there is a custom role that inherits ACCOUNTADMIN, you need to explicitly write that role name as well.

Comparing the 3 Predefined Scopes

Without writing your own YAML, you can reference Snowflake's 3 predefined scopes by name from a session policy. Apply each one to a user in turn and have CoCo execute the same SQL to observe the differences.

SELECT COUNT(*) FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS;
INSERT INTO KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_NOTES (note) VALUES ('P4 DATA_READ');
SELECT KAWABATA_RSS_GOV_DB.POLICIES.RSS_ECHO('P4');
CALL KAWABATA_RSS_GOV_DB.POLICIES.RSS_PING();
CREATE TABLE KAWABATA_RSS_SANDBOX_DB.WORK.AGENT_T3 (id INT);
SHOW TABLES IN SCHEMA KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES;

Since only one session policy can be assigned to a user at a time, swapping requires UNSET followed by SET.

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ';
CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ_WITH_AI
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ_WITH_AI';
CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ_PROGRAM_USAGE
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ_PROGRAM_USAGE';

ALTER USER KAWABATA_TOMOHIRO UNSET SESSION POLICY;
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_DATA_READ;
-- Similarly UNSET → SET for WITH_AI / PROGRAM_USAGE below

SNOWFLAKE$DATA_READ: data read only, across the entire account
2026-09-04_20h49_08

SNOWFLAKE$DATA_READ_WITH_AI: data read + object discovery + USAGE on AI-related objects (Agent, MCP Server, Cortex Search service)

2026-09-04_20h55_51

SNOWFLAKE$DATA_READ_PROGRAM_USAGE: data read + program usage (execution of UDFs, stored procedures, and Streamlit apps)

2026-09-04_20h58_13

A summary of the results for all three scopes is as follows.

Operation DATA_READ DATA_READ_WITH_AI DATA_READ_PROGRAM_USAGE
SELECT Success Success Success
SHOW TABLES Success Success Success
INSERT to sandbox Denied Denied Denied
UDF call Denied Denied Success
CALL stored procedure Denied Denied Success

UDF calls were denied even with SNOWFLAKE$DATA_READ_WITH_AI. While the main body of the official documentation contains wording that appears to include UDFs, it is believed that only USAGE on AI-related objects is permitted, as specified in the appendix YAML definition. It is recommended to perform actual testing on your target account when using this feature.
Note that CREATE TABLE failed all 3 times due to a name conflict with a previously created table (Object already exists), so it has been excluded from this table.

Inline YAML

We also tested a method of writing YAML directly into the session policy without creating an RSS object. In the AGENT_RESTRICTED_SESSION_SCOPE row of DESC SESSION POLICY, the YAML body itself is displayed as-is instead of an RSS name.

CREATE OR REPLACE SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_INLINE
  AGENT_RESTRICTED_SESSION_SCOPE = $$
privilege_scopes:
  allowed_privileges:
    - privileges: [data read]
      databases: [KAWABATA_RSS_PROD_DB]
$$;
DESC SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_INLINE;

2026-09-04_21h54_08

Masking Policies and RSS

Finally, we layer the existing masking policy that "masks only when an agent is active" together with RSS in the same session.
Since RSS controls "the scope of what can be accessed" and masking controls "the values visible after access," the two should not conflict but rather complement each other.

-- IS_AGENT_ACTIVATED returns either VARCHAR or BOOLEAN depending on whether Behavior Change Bundle 2026_06 is enabled, absorbed with ::BOOLEAN
CREATE OR REPLACE MASKING POLICY KAWABATA_RSS_GOV_DB.POLICIES.MASK_EMAIL_FOR_AGENT
  AS (val STRING) RETURNS STRING ->
  CASE
    WHEN SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEAN = TRUE
      THEN REGEXP_REPLACE(val, '^[^@]+', '***')
    ELSE val
  END;

ALTER TABLE KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  MODIFY COLUMN email SET MASKING POLICY KAWABATA_RSS_GOV_DB.POLICIES.MASK_EMAIL_FOR_AGENT;

-- Revert the session policy to UC-A's RSS_PROD_READONLY
ALTER USER KAWABATA_TOMOHIRO UNSET SESSION POLICY;
ALTER USER KAWABATA_TOMOHIRO SET SESSION POLICY KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY;

Output IS_AGENT_ACTIVATED, CURRENT_ROLE(), and email simultaneously from both CoCo and the worksheet, then execute an INSERT to production.

SELECT
  SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS IS_AGENT_ACTIVATED,
  CURRENT_ROLE() AS CURRENT_ROLE,
  order_id, customer_name, email
FROM KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
ORDER BY order_id
LIMIT 3;

INSERT INTO KAWABATA_RSS_PROD_DB.KAWABATA_RSS_SALES.ORDERS
  VALUES (903, 'Test Saburo', 'test903@example.com', CURRENT_DATE(), 'Test Item', 1.00, 'Test', 'RSS_TEST');

Masking Policy
2026-09-04_22h05_48

2026-09-04_22h07_00

INSERT
2026-09-04_22h09_57

2026-09-04_22h10_48

With the same user and same role, the agent alone is in a state where it "cannot write to production and cannot see email addresses," confirming that both layers of defense are operating simultaneously.

Checking Applied Status

The applied status can be verified using POLICY_REFERENCES. We tested both the method of looking up from the policy name as shown in the official documentation examples, and the reverse lookup method from the user side.

-- Check applied targets from the policy side
SELECT POLICY_NAME, POLICY_KIND, REF_ENTITY_NAME, REF_ENTITY_DOMAIN, POLICY_STATUS
FROM TABLE(KAWABATA_RSS_GOV_DB.INFORMATION_SCHEMA.POLICY_REFERENCES(
  POLICY_NAME => 'KAWABATA_RSS_GOV_DB.POLICIES.SP_AGENT_PROD_READONLY'));

2026-09-04_22h19_53


-- Reverse lookup from the user side
SELECT POLICY_NAME, POLICY_KIND, REF_ENTITY_NAME, REF_ENTITY_DOMAIN, POLICY_STATUS
FROM TABLE(KAWABATA_RSS_GOV_DB.INFORMATION_SCHEMA.POLICY_REFERENCES(
  REF_ENTITY_NAME => 'KAWABATA_TOMOHIRO', REF_ENTITY_DOMAIN => 'USER'));

2026-09-04_22h20_52

Looking up from the policy name returns 1 row for the applied user, while looking up in reverse from the user side returns 2 rows: the network policy and the session policy attached to that user.

Limitations and Notes

Limitations and Notes (click to expand)

Specifications documented in the official documentation

  • RSS is a ceiling on top of existing RBAC and does not add permissions. Even if permitted by RSS, operations cannot be executed without RBAC permissions
  • Once an RSS is activated for agent activity, it cannot be changed, removed, or elevated during that session. After modifying RSS, verify in a new conversation
  • The ALTER RESTRICTED SESSION SCOPE ... ADD AS $$...$$ syntax in the official documentation resulted in a syntax error on the verification account. It succeeded with ADD $$...$$ without AS (REMOVE AS has not been verified)
  • A user-level session policy completely overwrites the account-level policy and is not merged
  • blocked_roles performs an exact match on role names and does not traverse the role hierarchy. It also does not strip inherited permissions
  • RSS that is being referenced cannot be DROPped. First remove the reference from the session policy side
  • User-managed RSS in CoCo CLI / CoCo Desktop was in Private Preview as of September 4, 2026

Behavior confirmed during this verification (as of September 4, 2026)

  • SYS_CONTEXT('SNOWFLAKE$CURRENT', 'ACTIVE_RESTRICTED_SESSION_SCOPES') resulted in an invalid property error when referenced from a non-agent worksheet. Since the official documentation states it returns NULL when RSS is not active, this is believed to be a difference due to the release version or client at the time of verification. The return value in an agent session was not confirmed in this article
  • IS_AGENT_ACTIVATED returns VARCHAR in environments where Behavior Change Bundle 2026_06 is disabled. Use ::BOOLEAN to handle this within policies
  • Creating a table in a personal database (USER$) was denied on the verification account with 060119: Tables cannot currently be created in a personal database. Write permission to USER$ was not actually tested in this article

Items not covered in the verification

  • Priority between account-level and user-level session policies (did not execute ALTER ACCOUNT due to shared account)
  • Paths other than CoCo in Snowsight, such as CoCo CLI / CoCo Desktop, MCP Server, and SERVICE_AGENT users
  • Impact of applying RSS on query performance and costs

Closing Remarks

"What to prevent agents from doing" had previously been a binary choice: either separate roles, or detect after the fact. RSS places a third option in between: "restrict the operations available only when acting as an agent, while keeping the same role as a human."
I felt it is a great feature for governance in support of AI utilization.

I hope this article serves as a useful reference for someone!


Snowflake World Tour Tokyo 2026に参加しませんか?

Snowflakeの国内最大級イベント「Snowflake World Tour Tokyo」が2026年9月10日(木)・11日(金)にグランドプリンスホテル新高輪にて開催されます。
最新のAI・データ活用事例やライブデモを体感できる無料イベントです。

Snowflake World Tour Tokyoイベントに参加する


Snowflake Community Awards ファイナリストに選出されました

DevelopersIO で Snowflake 記事を執筆している かわばた が、Snowflake Community Awards「RISING COMMUNITY LEADER OF THE YEAR」部門・APJ枠のファイナリストに選ばれました。
最終選考の30%はコミュニティ投票です。記事がお役に立っていたようでしたら、9月15日(火)までにぜひ一票お願いします。フォームの「(4 of 6) RISING COMMUNITY LEADER OF THE YEAR」で Tomohiro Kawabata | Classmethod, Japan を選択、2分ほどで完了します。

投票フォームを開く


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

Share this article