Setup generators
This page contains interactive versions of the SQL scripts needed to set up the application. Fill in the names of your database, schemas and roles, and copy the generated script. The scripts are assembled in your browser and nothing is sent anywhere.
The fields accept identifier characters only; if an identifier needs double quotes in Snowflake, include the quotes in the field. Each generator takes one name per field. If a script has to cover more than one schema, repeat the schema-level statements as noted below.
For an explanation of what each grant does, see Administration.
Permission generator
This generator produces the grants the application needs on your data, and the roles your users need on the application. The grants are explained one by one in Table access privileges and Consumer roles and privileges.
If the application should read from several schemas, repeat the statements that name the read schema once per schema: GRANT USAGE ON SCHEMA, SELECT ON ALL TABLES, SELECT ON ALL VIEWS, SELECT ON FUTURE TABLES and SELECT ON FUTURE VIEWS.
If the results go into a schema you also read from, name it in both fields; the repeated GRANT USAGE ON SCHEMA is harmless.
-- Permission setup for {{app}}
USE ROLE {{privRole}};
-- Let the application see the database and its schemas
GRANT USAGE ON DATABASE {{db}} TO APPLICATION {{app}};
GRANT USAGE ON SCHEMA {{db}}.{{readSchema}} TO APPLICATION {{app}};
GRANT USAGE ON SCHEMA {{db}}.{{writeSchema}} TO APPLICATION {{app}};
-- Required to read tabular data into a graph
GRANT SELECT ON ALL TABLES IN SCHEMA {{db}}.{{readSchema}} TO APPLICATION {{app}};
GRANT SELECT ON ALL VIEWS IN SCHEMA {{db}}.{{readSchema}} TO APPLICATION {{app}};
-- Required to write computation results into a table
GRANT CREATE TABLE ON SCHEMA {{db}}.{{writeSchema}} TO APPLICATION {{app}};
-- Access to tables and views created later
CREATE DATABASE ROLE IF NOT EXISTS {{db}}.{{dbRole}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{db}}.{{readSchema}} TO DATABASE ROLE {{db}}.{{dbRole}};
GRANT SELECT ON FUTURE VIEWS IN SCHEMA {{db}}.{{readSchema}} TO DATABASE ROLE {{db}}.{{dbRole}};
GRANT DATABASE ROLE {{db}}.{{dbRole}} TO APPLICATION {{app}};
-- Consumer roles, to be granted to your users
CREATE ROLE IF NOT EXISTS {{userRole}};
GRANT APPLICATION ROLE {{app}}.app_user TO ROLE {{userRole}};
CREATE ROLE IF NOT EXISTS {{adminRole}};
GRANT APPLICATION ROLE {{app}}.app_admin TO ROLE {{adminRole}};
-- Let users read the result tables
GRANT USAGE ON DATABASE {{db}} TO ROLE {{userRole}};
GRANT USAGE ON SCHEMA {{db}}.{{writeSchema}} TO ROLE {{userRole}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{db}}.{{writeSchema}} TO ROLE {{userRole}};
{
"intro": "The script below updates as you type.",
"groups": [
{ "legend": "Application", "fields": ["app", "privRole"] },
{ "legend": "Consumer roles", "fields": ["userRole", "adminRole"] },
{ "legend": "Data to read", "fields": ["db", "readSchema", "dbRole"] },
{ "legend": "Where to write results", "fields": ["writeSchema"] }
],
"fields": {
"app": {
"label": "Application name",
"description": "The name the application is installed under.",
"default": "Neo4j_Graph_Analytics",
"pattern": "[A-Za-z0-9_$\"]"
},
"privRole": {
"label": "Privileged role running the script",
"description": "May grant on your database and create roles.",
"default": "ACCOUNTADMIN",
"pattern": "[A-Za-z0-9_$\"]"
},
"userRole": {
"label": "Consumer role for users",
"description": "Created by the script and granted the app_user application role.",
"placeholder": "<consumer_user_role>",
"pattern": "[A-Za-z0-9_$\"]"
},
"adminRole": {
"label": "Consumer role for administrators",
"description": "Created by the script and granted the app_admin application role.",
"placeholder": "<consumer_admin_role>",
"pattern": "[A-Za-z0-9_$\"]"
},
"db": {
"label": "Database holding your data",
"description": "Must already exist.",
"placeholder": "<database_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"readSchema": {
"label": "Schema to read from",
"description": "The schema with the node and relationship tables.",
"placeholder": "<schema_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"dbRole": {
"label": "Database role for future objects",
"description": "Created by the script; carries access to tables and views added later.",
"placeholder": "<database_role>",
"pattern": "[A-Za-z0-9_$\"]"
},
"writeSchema": {
"label": "Schema to write results into",
"description": "In the same database. May be the same as the read schema.",
"placeholder": "<write_schema_name>",
"pattern": "[A-Za-z0-9_$\"]"
}
}
}
Agent setup generator
This generator produces the script that sets up Neo4j Agent for CortexAI: the privileges the agent’s role and the application need, the CREATE_AGENT call, and the Restricted Caller’s Rights grants the agent’s _explore_graph_data tool depends on.
The same steps are written out in Initial Setup.
The script comes in two parts, because the role changes between them.
The grants come first and need a role that can create roles and grant on your databases — ACCOUNTADMIN in the script, or any role you have set up with those privileges plus MANAGE CALLER GRANTS for the caller grants at the end of part 1.
Then comes the CREATE_AGENT call, which must be run by the consumer role that will own the agent.
The USE ROLE statement in the middle switches over, so the script still runs top to bottom as one script.
The caller grants at the end of part 1 are the same statements that CREATE_AGENT returns and that GET_AGENT_CALLER_GRANTS reprints.
They do not depend on the agent existing, so granting them up front saves a trip back to ACCOUNTADMIN.
The script assumes that the source and working schemas live in different databases, and creates one database role in each, since a database role only carries privileges inside its own database.
If both schemas are in the same database, put the same name in both database fields, drop the second CREATE DATABASE ROLE block, and grant its statements to the first database role instead.
-- Agent setup for {{agent}} on {{app}}
-- Part 1: privileges and caller grants. Run as ACCOUNTADMIN.
USE ROLE ACCOUNTADMIN;
-- The role that creates the agent and converses with it
CREATE ROLE IF NOT EXISTS {{role}};
GRANT APPLICATION ROLE {{app}}.app_admin TO ROLE {{role}};
GRANT APPLICATION ROLE {{app}}.app_user TO ROLE {{role}};
GRANT DATABASE ROLE SNOWFLAKE.CORTEX_AGENT_USER TO ROLE {{role}};
GRANT USAGE ON WAREHOUSE {{wh}} TO ROLE {{role}};
SET agent_setup_user = (SELECT CURRENT_USER());
GRANT ROLE {{role}} TO USER IDENTIFIER($agent_setup_user);
-- Working area for the views and result tables the agent creates
CREATE DATABASE IF NOT EXISTS {{workDb}};
CREATE SCHEMA IF NOT EXISTS {{workDb}}.{{workSchema}};
-- Application access to the source schema
CREATE DATABASE ROLE IF NOT EXISTS {{srcDb}}.{{srcDbRole}};
GRANT USAGE ON DATABASE {{srcDb}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT USAGE ON SCHEMA {{srcDb}}.{{srcSchema}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT SELECT ON ALL TABLES IN SCHEMA {{srcDb}}.{{srcSchema}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT SELECT ON ALL VIEWS IN SCHEMA {{srcDb}}.{{srcSchema}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{srcDb}}.{{srcSchema}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT SELECT ON FUTURE VIEWS IN SCHEMA {{srcDb}}.{{srcSchema}} TO DATABASE ROLE {{srcDb}}.{{srcDbRole}};
GRANT DATABASE ROLE {{srcDb}}.{{srcDbRole}} TO APPLICATION {{app}};
GRANT DATABASE ROLE {{srcDb}}.{{srcDbRole}} TO ROLE {{role}};
-- The working database needs a database role of its own. If both schemas are in
-- the same database, drop this block and grant its statements to the role above.
CREATE DATABASE ROLE IF NOT EXISTS {{workDb}}.{{workDbRole}};
GRANT USAGE ON DATABASE {{workDb}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT USAGE ON SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT SELECT ON ALL TABLES IN SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT SELECT ON ALL VIEWS IN SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT SELECT ON FUTURE VIEWS IN SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT CREATE TABLE ON SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT CREATE VIEW ON SCHEMA {{workDb}}.{{workSchema}} TO DATABASE ROLE {{workDb}}.{{workDbRole}};
GRANT DATABASE ROLE {{workDb}}.{{workDbRole}} TO APPLICATION {{app}};
GRANT DATABASE ROLE {{workDb}}.{{workDbRole}} TO ROLE {{role}};
-- The consumer role reads the input data and the results itself
GRANT USAGE ON DATABASE {{srcDb}} TO ROLE {{role}};
GRANT USAGE ON DATABASE {{workDb}} TO ROLE {{role}};
GRANT USAGE ON SCHEMA {{srcDb}}.{{srcSchema}} TO ROLE {{role}};
GRANT SELECT ON ALL TABLES IN SCHEMA {{srcDb}}.{{srcSchema}} TO ROLE {{role}};
GRANT SELECT ON ALL VIEWS IN SCHEMA {{srcDb}}.{{srcSchema}} TO ROLE {{role}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{srcDb}}.{{srcSchema}} TO ROLE {{role}};
GRANT USAGE ON SCHEMA {{workDb}}.{{workSchema}} TO ROLE {{role}};
GRANT SELECT ON FUTURE TABLES IN SCHEMA {{workDb}}.{{workSchema}} TO ROLE {{role}};
GRANT SELECT ON FUTURE VIEWS IN SCHEMA {{workDb}}.{{workSchema}} TO ROLE {{role}};
-- The agent's _explore_graph_data tool runs under Restricted Caller's Rights and
-- cannot read these schemas without the grants below. They require ACCOUNTADMIN
-- or a role with MANAGE CALLER GRANTS.
GRANT CALLER USAGE ON DATABASE {{srcDb}} TO APPLICATION {{app}};
GRANT CALLER USAGE ON DATABASE {{workDb}} TO APPLICATION {{app}};
GRANT CALLER USAGE ON SCHEMA {{srcDb}}.{{srcSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL TABLES IN SCHEMA {{srcDb}}.{{srcSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL VIEWS IN SCHEMA {{srcDb}}.{{srcSchema}} TO APPLICATION {{app}};
GRANT CALLER USAGE ON SCHEMA {{workDb}}.{{workSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL TABLES IN SCHEMA {{workDb}}.{{workSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL VIEWS IN SCHEMA {{workDb}}.{{workSchema}} TO APPLICATION {{app}};
-- The caller grants are scoped to the application and the schema pair, not to one
-- agent, so they survive agent re-creation and application upgrades. Reprint them
-- at any time with CALL {{app}}.GRAPH.GET_AGENT_CALLER_GRANTS('{{agent}}');
-- Part 2: create the agent. Run as the consumer role that will own it, not as
-- ACCOUNTADMIN.
USE ROLE {{role}};
USE WAREHOUSE {{wh}};
USE SCHEMA {{workDb}}.{{workSchema}};
CALL {{app}}.GRAPH.CREATE_AGENT(
'{{agent}}',
'{{srcDb}}.{{srcSchema}}',
'{{workDb}}.{{workSchema}}'
);
-- The return value repeats the caller grants from part 1.
{
"intro": "The script below updates as you type.",
"groups": [
{ "legend": "Application and agent", "fields": ["app", "agent"] },
{ "legend": "Who runs the agent", "fields": ["role", "wh"] },
{ "legend": "Source schema", "fields": ["srcDb", "srcSchema", "srcDbRole"] },
{ "legend": "Working schema", "fields": ["workDb", "workSchema", "workDbRole"] }
],
"fields": {
"app": {
"label": "Application name",
"description": "The name the application is installed under.",
"default": "Neo4j_Graph_Analytics",
"pattern": "[A-Za-z0-9_$\"]"
},
"agent": {
"label": "Agent name",
"description": "The name to create the agent under.",
"placeholder": "<agent_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"role": {
"label": "Consumer role to create",
"description": "Owns the agent and converses with it. Created by the script and granted to you.",
"placeholder": "<consumer_role>",
"pattern": "[A-Za-z0-9_$\"]"
},
"wh": {
"label": "Warehouse for the session that creates the agent",
"description": "Any warehouse the consumer role can use. Must already exist.",
"default": "COMPUTE_WH",
"pattern": "[A-Za-z0-9_$\"]"
},
"srcDb": {
"label": "Source database",
"description": "Holds the read-only input data. Must already exist.",
"placeholder": "<source_db>",
"pattern": "[A-Za-z0-9_$\"]"
},
"srcSchema": {
"label": "Source schema",
"description": "Holds the input tables. Must already exist.",
"placeholder": "<source_schema>",
"pattern": "[A-Za-z0-9_$\"]"
},
"srcDbRole": {
"label": "Database role in the source database",
"description": "Created by the script; carries read access to the source schema.",
"placeholder": "<source_database_role>",
"pattern": "[A-Za-z0-9_$\"]"
},
"workDb": {
"label": "Working database",
"description": "Holds the views and results the agent writes. Created for you.",
"placeholder": "<working_db>",
"pattern": "[A-Za-z0-9_$\"]"
},
"workSchema": {
"label": "Working schema",
"description": "Created for you.",
"placeholder": "<working_schema>",
"pattern": "[A-Za-z0-9_$\"]"
},
"workDbRole": {
"label": "Database role in the working database",
"description": "Created by the script; carries read and write access to the working schema.",
"placeholder": "<working_database_role>",
"pattern": "[A-Za-z0-9_$\"]"
}
}
}
Per-user impersonation onboarding generator Preview
This generator produces the script that onboards a single user to execute-as-user. Repeat it for each user; only the install-level step is shared between them.
Unlike the two generators above, this script cannot be run in one go.
Part 1 mints the PAT and returns a token_secret column that Snowflake shows only once; you have to paste that value into part 2 before running it.
register_user_role in part 2 also has to run in a session opened as the user being onboarded, so that CURRENT_USER() resolves to them.
|
Copy |
Never paste a token into the form below; the placeholder in the generated script is intentional. If the user’s jobs touch more than one schema, repeat the caller grants in step 2e for each of them.
-- Part 1: enable PAT auth and mint the token. Run as ACCOUNTADMIN.
USE ROLE ACCOUNTADMIN;
-- Prerequisite: let the user mint and use a PAT without a network policy
CREATE AUTHENTICATION POLICY IF NOT EXISTS {{authPolicy}}
PAT_POLICY = (NETWORK_POLICY_EVALUATION = ENFORCED_NOT_REQUIRED);
ALTER USER {{user}} SET AUTHENTICATION POLICY {{authPolicy}};
-- Step 1: enable PAT auth on the install (once per account)
CALL {{app}}.preview.set_enable_custom_credentials(TRUE);
-- Step 2a: mint a PAT bound to the role
ALTER USER {{user}} ADD PROGRAMMATIC ACCESS TOKEN {{patName}}
DAYS_TO_EXPIRY = {{days}}
ROLE_RESTRICTION = '{{role}}';
-- ---------------------------------------------------------------------------
-- STOP. Run everything above first, then copy the token_secret value from the
-- result, because Snowflake will not show it again, and paste it into
-- SECRET_STRING below before running the rest.
-- ---------------------------------------------------------------------------
-- Part 2: store the token and register the user.
USE ROLE ACCOUNTADMIN;
-- Step 2b: stash the token in a secret you own
CREATE OR REPLACE SECRET {{secretDb}}.{{secretSchema}}.{{secretName}}
TYPE = GENERIC_STRING
SECRET_STRING = '<paste_token_secret_from_part_1_here>';
-- Step 2c: let the application read the secret
GRANT USAGE ON DATABASE {{secretDb}} TO APPLICATION {{app}};
GRANT USAGE ON SCHEMA {{secretDb}}.{{secretSchema}} TO APPLICATION {{app}};
GRANT READ ON SECRET {{secretDb}}.{{secretSchema}}.{{secretName}} TO APPLICATION {{app}};
-- Step 2d: register the user with the application. Run this one in a session
-- opened as {{user}}, so that CURRENT_USER() resolves to them.
CALL {{app}}.preview.register_user_role(
'{{role}}',
'{{secretDb}}.{{secretSchema}}.{{secretName}}'
);
-- Step 2e: caller grants for the data the jobs read and write
USE ROLE ACCOUNTADMIN;
GRANT CALLER USAGE ON DATABASE {{dataDb}} TO APPLICATION {{app}};
GRANT CALLER USAGE ON SCHEMA {{dataDb}}.{{dataSchema}} TO APPLICATION {{app}};
GRANT CALLER CREATE TABLE ON SCHEMA {{dataDb}}.{{dataSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER INSERT ON ALL TABLES IN SCHEMA {{dataDb}}.{{dataSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL TABLES IN SCHEMA {{dataDb}}.{{dataSchema}} TO APPLICATION {{app}};
GRANT INHERITED CALLER SELECT ON ALL VIEWS IN SCHEMA {{dataDb}}.{{dataSchema}} TO APPLICATION {{app}};
-- Step 3: after the user has run a job, check whose identity it used
SELECT user_name, role_name, LEFT(query_text, 120) AS sql, start_time
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE start_time > DATEADD(minute, -30, CURRENT_TIMESTAMP())
AND role_name = '{{role}}'
ORDER BY start_time DESC LIMIT 20;
{
"intro": "The script below updates as you type.",
"groups": [
{ "legend": "Application", "fields": ["app", "authPolicy"] },
{ "legend": "User and token", "fields": ["user", "role", "patName", "days"] },
{ "legend": "Where the secret lives", "fields": ["secretDb", "secretSchema", "secretName"] },
{ "legend": "Data the user's jobs touch", "fields": ["dataDb", "dataSchema"] }
],
"fields": {
"app": {
"label": "Application name",
"description": "The name the application is installed under.",
"default": "Neo4j_Graph_Analytics",
"pattern": "[A-Za-z0-9_$\"]"
},
"authPolicy": {
"label": "Authentication policy to create",
"description": "Lets the user mint and use a PAT without a network policy.",
"default": "pat_no_network_required",
"pattern": "[A-Za-z0-9_$\"]"
},
"user": {
"label": "User to onboard",
"description": "The Snowflake user whose identity the jobs will run under.",
"placeholder": "<user_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"role": {
"label": "Role the user's jobs run under",
"description": "The token is restricted to this role.",
"placeholder": "<role_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"patName": {
"label": "Token name",
"description": "Scoped to the user, so the same name can be used for every user.",
"default": "app_pat",
"pattern": "[A-Za-z0-9_$\"]"
},
"days": {
"label": "Days to expiry",
"description": "How long the token stays valid.",
"default": "365",
"pattern": "[0-9]"
},
"secretDb": {
"label": "Database for the secret",
"description": "Must already exist.",
"placeholder": "<db>",
"pattern": "[A-Za-z0-9_$\"]"
},
"secretSchema": {
"label": "Schema for the secret",
"description": "Must already exist.",
"placeholder": "<schema>",
"pattern": "[A-Za-z0-9_$\"]"
},
"secretName": {
"label": "Secret name",
"description": "One secret per user, so include the user name.",
"placeholder": "pat_secret_<user_name>",
"pattern": "[A-Za-z0-9_$\"]"
},
"dataDb": {
"label": "Database",
"description": "Holds the tables the user's jobs read and write.",
"placeholder": "<data_db>",
"pattern": "[A-Za-z0-9_$\"]"
},
"dataSchema": {
"label": "Schema",
"description": "Repeat the caller grants in step 2e for each further schema.",
"placeholder": "<data_schema>",
"pattern": "[A-Za-z0-9_$\"]"
}
}
}